QA Interview Hub · База знань · Тести

Selenium + Java

Фреймворк і запуски

JUnit 5 проти TestNG

Selenium лише керує браузером; запускає тести, готує дані й збирає звіти тестовий фреймворк. Два поширені в Java: JUnit 5 і TestNG.

JUnit 5TestNG
Підготовка/завершення@BeforeEach, @AfterEach, @BeforeAll, @AfterAll (static, якщо немає PER_CLASS)@BeforeMethod, @AfterMethod, @BeforeClass, @BeforeSuite, @BeforeTest
Параметризація@ParameterizedTest + @ValueSource, @CsvSource, @MethodSource@DataProvider (можна parallel = true)
Групування@Tag + фільтри в збірціgroups, testng.xml
Залежності тестівсвідомо немає (тести мають бути незалежними)dependsOnMethods, dependsOnGroups
Розширеннямодель Extension API (@ExtendWith, TestWatcher, ParameterResolver)слухачі (ITestListener, IRetryAnalyzer)
Паралельністьjunit-platform.propertiesатрибути parallel і thread-count у testng.xml
@ParameterizedTest
@CsvSource({"admin,Admin panel", "guest,Welcome"})
void showsRoleHome(String role, String heading) {
    // ...
}

Нові проєкти найчастіше беруть JUnit Jupiter (JUnit 5, тепер JUnit 6 з тим самим API): ширша екосистема, інтеграція з IDE та збіркою, зручні розширення. TestNG лишається у великих існуючих наборах, де потрібні групи, залежності й вбудований testng.xml. У JUnit 5 немає вбудованого retry: його додають розширенням або повтором упалих тестів на рівні Surefire чи CI.

Кожен тест має створювати власний стан і не залежати від порядку: це перша умова паралельного запуску.

Selenium Grid 4 і Docker: як влаштований і як запускати

Selenium Grid маршрутизує команди клієнта на віддалені браузери: паралельні запуски на багатьох машинах, різні версії браузерів і ОС. Тест підключається через RemoteWebDriver до одного URL (типово порт 4444), а решту робить Grid.

Три режими розгортання Grid 4:

  • Standalone — усе в одному процесі; найпростіше для розробки й малих наборів;
  • Hub and Node — один Hub і кілька Node (кожен із власними браузерами, можливо різні ОС);
  • Distributed — компоненти окремими процесами для великого масштабу.

Компоненти Grid 4: Router (приймає запити, спрямовує їх), Distributor (знаходить вільний слот на Node), Session Map (хто з сесій на якому Node), New Session Queue (черга запитів на нові сесії, коли слотів немає), Node (запускає браузери), Event Bus (обмін подіями між компонентами). Стан видно на /status і в UI на /ui.

Docker. Готові образи selenium/standalone-chrome, selenium/standalone-firefox, selenium/hub + node-chrome дають браузер без встановлення на машину.

docker run -d -p 4444:4444 -p 7900:7900 --shm-size="2g" selenium/standalone-chrome
  • --shm-size (або /dev/shm) потрібен, бо Chrome активно користується спільною пам’яттю; з малим обсягом браузер падає на важких сторінках;
  • порт 7900 — перегляд сесії через noVNC у браузері, зручно для дебагу;
  • за замовчуванням контейнер виконує одну сесію (SE_NODE_MAX_SESSIONS); більше дозволяє ця змінна, але межу задають ресурси контейнера.

У CI зазвичай піднімають Grid як сервіс (Docker Compose чи сервіс-контейнер джоби), чекають готовності на /status і запускають тести з URL Grid. Для завантаження файлів на віддалену машину потрібен LocalFileDetector.

Паралельні запуски: драйвер на потік, ізоляція, CI

WebDriver не потокобезпечний: одну сесію не можна ділити між потоками. Правило паралелізму: один драйвер на тест (потік), створений перед тестом і закритий після нього.

Типова пастка — static WebDriver driver у базовому класі або Page Object: паралельні потоки перезаписують одне посилання й керують чужим браузером. Рішення:

  • створювати драйвер у @BeforeEach (JUnit) / @BeforeMethod (TestNG) і передавати в Page Object через конструктор;
  • або тримати його в ThreadLocal<WebDriver> — тоді кожен потік бачить свій екземпляр, а remove() після тесту запобігає витоку;
  • у JUnit 5 зручно оформити драйвер як розширення з ParameterResolver, що створює й закриває його.
private static final ThreadLocal<WebDriver> DRIVER = new ThreadLocal<>();

static WebDriver driver() { return DRIVER.get(); }

@BeforeEach void start() { DRIVER.set(new ChromeDriver()); }
@AfterEach  void stop()  { DRIVER.get().quit(); DRIVER.remove(); }

Вмикання паралельності: у JUnit 5 — junit.jupiter.execution.parallel.enabled=true і mode.default=concurrent у junit-platform.properties; у TestNG — parallel="methods" (або classes, tests) і thread-count у testng.xml.

Паралельність вимагає ізоляції даних: тести не діляться акаунтами, замовленнями, файлами. Інакше з’являються флейки, які не відтворюються поодинці. Кількість потоків узгоджують із ресурсами Grid або CI-раннера.

У CI: headless або Grid у контейнері, кеш Selenium Manager між запусками, артефакти (скриншоти, логи, звіт) зберігаються навіть для упалих джоб, повторний запуск лише упалих тестів позначають у звіті як flaky, а не приховують.