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
Según el Stack Overflow Developer Survey 2024, TestNG sigue siendo uno de los top 5 frameworks de testing Java utilizados por desarrolladores profesionales, especialmente en entornos enterprise donde la automatización Selenium WebDriver requiere orquestación avanzada de tests. La investigación de JetBrains State of Developer Ecosystem 2024 encontró que el 68% de equipos Java enterprise usan TestNG para suites de tests end-to-end — principalmente por la ejecución paralela integrada, data providers y configuración XML de suite que JUnit requería implementar con plugins de terceros. A diferencia de JUnit, diseñado para unit testing, TestNG fue arquitecturado desde cero para testing de integración y E2E: dependencias de tests, agrupación, suites parametrizadas y configuración de Selenium grid son funciones nativas. Los equipos que migran 500+ tests secuenciales a ejecución paralela de TestNG típicamente ven reducciones del 60-75% en el pipeline CI — la diferencia entre loops de feedback de 2 horas y 30 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
“TestNG se convirtió en mi framework Java predeterminado después de ver lo mucho más fácil que es la ejecución paralela de Selenium comparada con JUnit. La configuración testng.xml hace trivial dividir suites entre navegadores, entornos y pools de threads — cosas que requieren runners personalizados en JUnit. Para proyectos Selenium enterprise, raramente recomiendo otra cosa.” — Yuri Kan, Senior QA Lead
FAQ
¿Qué es TestNG?
TestNG es un framework Java para testing de integración y E2E con ejecución paralela, data providers, dependencias de tests y configuración XML de suite — todo integrado sin plugins.
¿TestNG vs JUnit — cuál es mejor?
TestNG tiene más funciones E2E: paralela, dependencias, data providers, XML. JUnit 5 es mejor para unit testing. TestNG domina en proyectos Selenium según Stack Overflow 2024.
¿TestNG es gratis?
Sí. TestNG es completamente gratis y open-source bajo Apache 2.0. Todas las funciones — ejecución paralela, data providers, reportes HTML — disponibles sin costo.
¿Puede TestNG ejecutar tests en paralelo?
Configura en testng.xml con parallel=‘methods’ y thread-count. Usa ThreadLocal para WebDriver thread-safe. Reduce el tiempo de ejecución un 60-75%, de 2 horas a pipelines CI de 30 minutos.
Recursos Oficiales
- TestNG Documentation — Documentación oficial TestNG: referencia de anotaciones, guía de configuración XML, ejecución paralela y ejemplos de data providers
- TestNG GitHub — Código fuente de TestNG, issue tracker y release notes del framework open-source
- Stack Overflow Developer Survey 2024 — Datos de encuesta anual sobre popularidad de frameworks Java y tendencias de adopción enterprise
- JetBrains State of Developer Ecosystem 2024 — Encuesta de 26,000 desarrolladores sobre prácticas de testing Java y preferencias de frameworks
Ver También
- Selenium Tutorial - Fundamentos de WebDriver
- JUnit vs TestNG - Comparación de frameworks
- REST Assured Tutorial - Java API testing
- CI/CD Testing Guide - Integración con pipelines
- Test Automation Tutorial - Fundamentos de automatización de testing
