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-инстансов.

Официальные ресурсы

Смотрите также