TL;DR

  • TestNG es un poderoso framework Java con rico soporte de anotaciones
  • Anotaciones clave: @Test, @BeforeMethod, @AfterMethod, @DataProvider
  • Configura test suites vía testng.xml para agrupación, paralelización, parámetros
  • Data providers habilitan data-driven testing con múltiples conjuntos de datos
  • Integración perfecta con Selenium WebDriver para automatización de navegador

Ideal para: Desarrolladores Java, automatización Selenium, frameworks de testing enterprise Omite si: Usas Kotlin (prefiere Kotest) o tests unitarios simples (JUnit es suficiente) Tiempo de lectura: 15 minutos

Tus tests de Selenium se ejecutan secuencialmente. 500 tests toman 2 horas. El pipeline CI/CD bloquea cada PR. Los desarrolladores ignoran resultados de tests porque el feedback llega muy tarde.

TestNG resuelve problemas de ejecución de tests. Ejecución paralela, configuración flexible, data-driven testing — todo integrado. Sin frameworks adicionales necesarios.

Este tutorial cubre TestNG desde setup hasta funciones avanzadas — todo para construir automatización de tests escalable.

¿Qué es TestNG?

TestNG (Test Next Generation) es un framework de testing para Java inspirado en JUnit y NUnit. Va más allá del testing unitario simple con funciones diseñadas para testing de integración y end-to-end.

Por qué TestNG:

  • Anotaciones ricas — @Test, @Before/@After en múltiples niveles
  • Ejecución paralela — soporte integrado para testing concurrente
  • Data providers — data-driven testing elegante
  • Dependencias de tests — control del orden de ejecución
  • Configuración XML — organización flexible de suites
  • Reportes HTML — reportes integrados

Instalación

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()
}

Anotaciones Básicas

@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)  // Saltar este test
    public void testDivision() {
        // Aún no implementado
    }

    @Test(expectedExceptions = ArithmeticException.class)
    public void testDivideByZero() {
        Calculator calc = new Calculator();
        calc.divide(10, 0);
    }
}

@Before y @After

import org.testng.annotations.*;

public class DatabaseTest {

    private Connection connection;

    @BeforeSuite
    public void setupSuite() {
        // Se ejecuta una vez antes de todos los tests en suite
        System.out.println("Iniciando test suite");
    }

    @BeforeClass
    public void setupClass() {
        // Se ejecuta una vez antes del primer test en clase
        connection = Database.connect();
    }

    @BeforeMethod
    public void setupMethod() {
        // Se ejecuta antes de cada método @Test
        connection.beginTransaction();
    }

    @Test
    public void testInsert() {
        // Código del test
    }

    @Test
    public void testUpdate() {
        // Código del test
    }

    @AfterMethod
    public void teardownMethod() {
        // Se ejecuta después de cada método @Test
        connection.rollback();
    }

    @AfterClass
    public void teardownClass() {
        // Se ejecuta una vez después del último test en clase
        connection.close();
    }

    @AfterSuite
    public void teardownSuite() {
        // Se ejecuta una vez después de todos los tests en suite
        System.out.println("Suite completado");
    }
}

Orden de Ejecución

@BeforeSuite
  @BeforeClass
    @BeforeMethod
      @Test (método 1)
    @AfterMethod
    @BeforeMethod
      @Test (método 2)
    @AfterMethod
  @AfterClass
@AfterSuite

Data Providers

Data Provider Básico

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,
            "Login falló para: " + email);
    }
}

Data Provider Externo

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][]);
    }
}

// Uso en otra clase
public class UserTest {
    @Test(dataProvider = "csvData", dataProviderClass = TestDataProvider.class)
    public void testUserCreation(String name, String email, String role) {
        // Test con datos CSV
    }
}

Configuración de Tests (testng.xml)

Configuración Básica

<?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>

Parámetros

<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() {
        // Test se ejecuta con navegador configurado
    }
}

Ejecución Paralela

Opciones de Configuración

<!-- Paralelo por métodos -->
<suite name="Parallel Suite" parallel="methods" thread-count="4">
    <test name="Tests">
        <classes>
            <class name="com.example.tests.LoginTest"/>
        </classes>
    </test>
</suite>

<!-- Paralelo por clases -->
<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>

WebDriver Thread-Safe

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");
        // Test es thread-safe
    }
}

Grupos de Tests

Definiendo Grupos

public class UserTest {

    @Test(groups = {"smoke", "regression"})
    public void testUserLogin() {
        // Test crítico - se ejecuta en ambos grupos
    }

    @Test(groups = {"regression"})
    public void testUserProfile() {
        // Solo en regression
    }

    @Test(groups = {"regression", "slow"})
    public void testUserReports() {
        // Test lento
    }
}

Ejecutando Grupos

<suite name="Smoke Suite">
    <test name="Smoke Tests">
        <groups>
            <run>
                <include name="smoke"/>
            </run>
        </groups>
        <packages>
            <package name="com.example.tests.*"/>
        </packages>
    </test>
</suite>

<!-- Excluir tests lentos -->
<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>

Dependencias de Tests

public class OrderTest {

    @Test
    public void testLogin() {
        // Login primero
    }

    @Test(dependsOnMethods = {"testLogin"})
    public void testAddToCart() {
        // Se ejecuta después de login exitoso
    }

    @Test(dependsOnMethods = {"testAddToCart"})
    public void testCheckout() {
        // Se ejecuta después de agregar al carrito
    }

    @Test(dependsOnMethods = {"testCheckout"})
    public void testPayment() {
        // Paso final
    }
}

Integración con Selenium

Ejemplo Completo

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 con Asistencia de IA

Las herramientas de IA pueden ayudar a escribir y optimizar tests de TestNG.

Lo que la IA hace bien:

  • Generar métodos de test desde requerimientos
  • Crear data providers con edge cases
  • Sugerir patrones de assertions
  • Convertir tests de JUnit a TestNG

Lo que aún necesita humanos:

  • Entender lógica de negocio
  • Elegir granularidad de tests
  • Balancear cobertura vs mantenimiento
  • Depurar tests inestables

FAQ

¿Qué es TestNG?

TestNG (Test Next Generation) es un framework de testing Java inspirado en JUnit y NUnit. Provee funciones poderosas más allá del testing unitario simple: anotaciones para gestión de ciclo de vida, ejecución paralela de tests, data-driven testing vía data providers, dependencias de tests y configuración XML flexible para organizar test suites. Es ampliamente usado con Selenium para automatización de navegador.

¿TestNG vs JUnit — cuál es mejor?

TestNG ofrece más funciones integradas: ejecución paralela, dependencias de tests, data providers y configuración XML flexible. JUnit 5 ha agregado muchas funciones similares, reduciendo la brecha. TestNG sigue siendo popular para proyectos Selenium por su configuración de suites XML y ecosistema establecido. Para proyectos nuevos, ambos funcionan bien — elige según familiaridad del equipo.

¿TestNG es gratis?

Sí, TestNG es completamente gratis y open-source bajo licencia Apache 2.0. No hay versiones pagas, ediciones enterprise ni funciones premium. Toda la funcionalidad — ejecución paralela, data providers, reportes — está disponible sin costo.

¿Puede TestNG ejecutar tests en paralelo?

Sí, TestNG tiene excelente soporte integrado de ejecución paralela. Configura el paralelismo a nivel de método, clase o test vía testng.xml. Establece el conteo de threads según las capacidades de tu máquina. Para tests de Selenium, usa ThreadLocal para asegurar instancias de WebDriver thread-safe.

Recursos Oficiales

Ver También