TL;DR
- TestNG — мощный Java-фреймворк с богатой поддержкой аннотаций
- Ключевые аннотации: @Test, @BeforeMethod, @AfterMethod, @DataProvider
- Настройка test suites через testng.xml для группировки, параллелизации, параметров
- Data providers позволяют data-driven тестирование с множеством входных данных
- Бесшовная интеграция с Selenium WebDriver для браузерной автоматизации
Идеально для: Java-разработчиков, Selenium-автоматизации, enterprise test frameworks Пропусти, если: Используешь Kotlin (лучше Kotest) или простые unit-тесты (достаточно JUnit) Время чтения: 15 минут
Твои Selenium-тесты запускаются последовательно. 500 тестов занимают 2 часа. CI/CD-пайплайн блокирует каждый PR. Разработчики игнорируют результаты тестов, потому что обратная связь приходит слишком поздно.
TestNG решает проблемы выполнения тестов. Параллельное выполнение, гибкая конфигурация, data-driven тестирование — всё встроено. Дополнительные фреймворки не нужны.
Этот туториал покрывает TestNG от настройки до продвинутых функций — всё для построения масштабируемой тестовой автоматизации.
Что такое TestNG?
TestNG (Test Next Generation) — это Java-фреймворк для тестирования, вдохновлённый JUnit и NUnit. Он выходит за рамки простого unit-тестирования с функциями для интеграционного и end-to-end тестирования.
Почему TestNG:
- Богатые аннотации — @Test, @Before/@After на множестве уровней
- Параллельное выполнение — встроенная поддержка конкурентного тестирования
- Data providers — элегантное data-driven тестирование
- Зависимости тестов — контроль порядка выполнения
- XML-конфигурация — гибкая организация suites
- HTML-отчёты — встроенная отчётность
Установка
Maven
<dependencies>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.9.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
</plugins>
</build>
Gradle
dependencies {
testImplementation 'org.testng:testng:7.9.0'
}
test {
useTestNG()
}
Базовые аннотации
@Test
import org.testng.annotations.Test;
import static org.testng.Assert.*;
public class CalculatorTest {
@Test
public void testAddition() {
Calculator calc = new Calculator();
assertEquals(calc.add(2, 3), 5);
}
@Test
public void testSubtraction() {
Calculator calc = new Calculator();
assertEquals(calc.subtract(5, 3), 2);
}
@Test(enabled = false) // Пропустить этот тест
public void testDivision() {
// Ещё не реализовано
}
@Test(expectedExceptions = ArithmeticException.class)
public void testDivideByZero() {
Calculator calc = new Calculator();
calc.divide(10, 0);
}
}
@Before и @After
import org.testng.annotations.*;
public class DatabaseTest {
private Connection connection;
@BeforeSuite
public void setupSuite() {
// Выполняется один раз перед всеми тестами в suite
System.out.println("Запуск тестового suite");
}
@BeforeClass
public void setupClass() {
// Выполняется один раз перед первым тестом в классе
connection = Database.connect();
}
@BeforeMethod
public void setupMethod() {
// Выполняется перед каждым @Test методом
connection.beginTransaction();
}
@Test
public void testInsert() {
// Код теста
}
@Test
public void testUpdate() {
// Код теста
}
@AfterMethod
public void teardownMethod() {
// Выполняется после каждого @Test метода
connection.rollback();
}
@AfterClass
public void teardownClass() {
// Выполняется один раз после последнего теста в классе
connection.close();
}
@AfterSuite
public void teardownSuite() {
// Выполняется один раз после всех тестов в suite
System.out.println("Suite завершён");
}
}
Порядок выполнения
@BeforeSuite
@BeforeClass
@BeforeMethod
@Test (метод 1)
@AfterMethod
@BeforeMethod
@Test (метод 2)
@AfterMethod
@AfterClass
@AfterSuite
Data Providers
Базовый Data Provider
import org.testng.annotations.*;
public class LoginTest {
@DataProvider(name = "loginCredentials")
public Object[][] provideCredentials() {
return new Object[][] {
{"user1@example.com", "password1", true},
{"user2@example.com", "password2", true},
{"invalid@example.com", "wrongpass", false},
{"", "password", false},
{"user@example.com", "", false}
};
}
@Test(dataProvider = "loginCredentials")
public void testLogin(String email, String password, boolean expectedResult) {
LoginPage loginPage = new LoginPage(driver);
boolean result = loginPage.login(email, password);
assertEquals(result, expectedResult,
"Ошибка логина для: " + email);
}
}
Внешний Data Provider
public class TestDataProvider {
@DataProvider(name = "csvData")
public static Object[][] readCsvData() throws IOException {
List<Object[]> data = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(
new FileReader("testdata/users.csv"))) {
String line;
while ((line = reader.readLine()) != null) {
String[] values = line.split(",");
data.add(values);
}
}
return data.toArray(new Object[0][]);
}
}
// Использование в другом классе
public class UserTest {
@Test(dataProvider = "csvData", dataProviderClass = TestDataProvider.class)
public void testUserCreation(String name, String email, String role) {
// Тест с CSV-данными
}
}
Конфигурация тестов (testng.xml)
Базовая конфигурация
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Regression Suite" verbose="1">
<test name="Login Tests">
<classes>
<class name="com.example.tests.LoginTest"/>
<class name="com.example.tests.LogoutTest"/>
</classes>
</test>
<test name="Dashboard Tests">
<classes>
<class name="com.example.tests.DashboardTest"/>
</classes>
</test>
</suite>
Параметры
<suite name="Cross Browser Suite">
<test name="Chrome Tests">
<parameter name="browser" value="chrome"/>
<parameter name="baseUrl" value="https://example.com"/>
<classes>
<class name="com.example.tests.LoginTest"/>
</classes>
</test>
<test name="Firefox Tests">
<parameter name="browser" value="firefox"/>
<parameter name="baseUrl" value="https://example.com"/>
<classes>
<class name="com.example.tests.LoginTest"/>
</classes>
</test>
</suite>
public class LoginTest {
private WebDriver driver;
@Parameters({"browser", "baseUrl"})
@BeforeMethod
public void setup(String browser, String baseUrl) {
if (browser.equals("chrome")) {
driver = new ChromeDriver();
} else if (browser.equals("firefox")) {
driver = new FirefoxDriver();
}
driver.get(baseUrl);
}
@Test
public void testLogin() {
// Тест запускается с настроенным браузером
}
}
Параллельное выполнение
Опции конфигурации
<!-- Параллельно по методам -->
<suite name="Parallel Suite" parallel="methods" thread-count="4">
<test name="Tests">
<classes>
<class name="com.example.tests.LoginTest"/>
</classes>
</test>
</suite>
<!-- Параллельно по классам -->
<suite name="Parallel Suite" parallel="classes" thread-count="4">
<test name="Tests">
<classes>
<class name="com.example.tests.LoginTest"/>
<class name="com.example.tests.DashboardTest"/>
</classes>
</test>
</suite>
Thread-Safe WebDriver
public class BaseTest {
protected static ThreadLocal<WebDriver> driver = new ThreadLocal<>();
@BeforeMethod
public void setup() {
WebDriver webDriver = new ChromeDriver();
driver.set(webDriver);
}
@AfterMethod
public void teardown() {
if (driver.get() != null) {
driver.get().quit();
driver.remove();
}
}
protected WebDriver getDriver() {
return driver.get();
}
}
public class LoginTest extends BaseTest {
@Test
public void testLogin() {
getDriver().get("https://example.com");
// Тест потокобезопасен
}
}
Группы тестов
Определение групп
public class UserTest {
@Test(groups = {"smoke", "regression"})
public void testUserLogin() {
// Критичный тест - запускается в обеих группах
}
@Test(groups = {"regression"})
public void testUserProfile() {
// Только в regression
}
@Test(groups = {"regression", "slow"})
public void testUserReports() {
// Медленный тест
}
}
Запуск групп
<suite name="Smoke Suite">
<test name="Smoke Tests">
<groups>
<run>
<include name="smoke"/>
</run>
</groups>
<packages>
<package name="com.example.tests.*"/>
</packages>
</test>
</suite>
<!-- Исключить медленные тесты -->
<suite name="Fast Suite">
<test name="Fast Tests">
<groups>
<run>
<include name="regression"/>
<exclude name="slow"/>
</run>
</groups>
<packages>
<package name="com.example.tests.*"/>
</packages>
</test>
</suite>
Зависимости тестов
public class OrderTest {
@Test
public void testLogin() {
// Сначала логин
}
@Test(dependsOnMethods = {"testLogin"})
public void testAddToCart() {
// Запускается после успешного логина
}
@Test(dependsOnMethods = {"testAddToCart"})
public void testCheckout() {
// Запускается после добавления в корзину
}
@Test(dependsOnMethods = {"testCheckout"})
public void testPayment() {
// Финальный шаг
}
}
Интеграция с Selenium
Полный пример
public class SeleniumBaseTest {
protected WebDriver driver;
protected WebDriverWait wait;
@BeforeMethod
public void setup() {
ChromeOptions options = new ChromeOptions();
options.addArguments("--headless");
driver = new ChromeDriver(options);
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
wait = new WebDriverWait(driver, Duration.ofSeconds(15));
}
@AfterMethod
public void teardown() {
if (driver != null) {
driver.quit();
}
}
}
public class LoginTest extends SeleniumBaseTest {
@Test(groups = {"smoke"})
public void testSuccessfulLogin() {
driver.get("https://example.com/login");
driver.findElement(By.id("email")).sendKeys("user@example.com");
driver.findElement(By.id("password")).sendKeys("password123");
driver.findElement(By.id("login-btn")).click();
WebElement welcome = wait.until(
ExpectedConditions.visibilityOfElementLocated(By.id("welcome"))
);
assertTrue(welcome.isDisplayed());
assertTrue(welcome.getText().contains("Welcome"));
}
}
TestNG с помощью ИИ
ИИ-инструменты могут помочь писать и оптимизировать TestNG-тесты.
Что ИИ делает хорошо:
- Генерация тестовых методов из требований
- Создание data providers с edge cases
- Предложение паттернов assertions
- Конвертация JUnit-тестов в TestNG
Что всё ещё требует людей:
- Понимание бизнес-логики
- Выбор гранулярности тестов
- Баланс покрытия и поддержки
- Отладка нестабильных тестов
FAQ
Что такое TestNG?
TestNG (Test Next Generation) — это Java-фреймворк для тестирования, вдохновлённый JUnit и NUnit. Он предоставляет мощные функции помимо простого unit-тестирования: аннотации для управления жизненным циклом, параллельное выполнение тестов, data-driven тестирование через data providers, зависимости тестов и гибкую XML-конфигурацию для организации test suites. Широко используется с Selenium для браузерной автоматизации.
TestNG или JUnit — что лучше?
TestNG предлагает больше встроенных функций: параллельное выполнение, зависимости тестов, data providers и гибкую XML-конфигурацию. JUnit 5 добавил много похожих функций, сократив разрыв. TestNG остаётся популярным для Selenium-проектов благодаря XML-конфигурации suites и устоявшейся экосистеме. Для новых проектов оба варианта хороши — выбирай на основе опыта команды.
TestNG бесплатный?
Да, TestNG полностью бесплатен и open-source под лицензией Apache 2.0. Нет платных версий, enterprise-редакций или премиум-функций. Вся функциональность — параллельное выполнение, data providers, отчётность — доступна бесплатно.
Может ли TestNG запускать тесты параллельно?
Да, TestNG имеет отличную встроенную поддержку параллельного выполнения. Настрой параллелизм на уровне методов, классов или тестов через testng.xml. Установи количество потоков в зависимости от возможностей машины. Для Selenium-тестов используй ThreadLocal для потокобезопасных WebDriver-инстансов.
Официальные ресурсы
Смотрите также
- Selenium Tutorial - Основы WebDriver
- JUnit vs TestNG - Сравнение фреймворков
- REST Assured Tutorial - Java API тестирование
- CI/CD Testing Guide - Интеграция с пайплайнами
