Skip to main content
SeleniumDecoded

TestNG and JUnit Integration

Integrate Selenium with TestNG and JUnit test frameworks for structured, maintainable Java tests.

Selenium 3 & 4 Stable Verified against Selenium 4.48.0

TestNG and JUnit are the two most popular testing frameworks for Java. Both integrate seamlessly with Selenium to provide test organization, assertions, and reporting.

TestNG Setup

Maven Dependencies

<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.18.1</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.9.0</version>
<scope>test</scope>
</dependency>
</dependencies>

Basic TestNG Test

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.By;
import org.testng.annotations.*;
import org.testng.Assert;
public class LoginTest {
private WebDriver driver;
@BeforeClass
public void setupClass() {
// Runs once before all tests in this class
System.out.println("Setting up test class");
}
@BeforeMethod
public void setup() {
// Runs before each test method
driver = new ChromeDriver();
driver.manage().window().maximize();
}
@Test(priority = 1)
public void testValidLogin() {
driver.get("https://example.com/login");
driver.findElement(By.id("username")).sendKeys("validuser");
driver.findElement(By.id("password")).sendKeys("validpass");
driver.findElement(By.id("login")).click();
Assert.assertTrue(driver.getCurrentUrl().contains("/dashboard"),
"Should redirect to dashboard");
}
@Test(priority = 2)
public void testInvalidLogin() {
driver.get("https://example.com/login");
driver.findElement(By.id("username")).sendKeys("invalid");
driver.findElement(By.id("password")).sendKeys("wrong");
driver.findElement(By.id("login")).click();
String error = driver.findElement(By.className("error")).getText();
Assert.assertEquals(error, "Invalid credentials");
}
@AfterMethod
public void teardown() {
// Runs after each test method
if (driver != null) {
driver.quit();
}
}
@AfterClass
public void teardownClass() {
// Runs once after all tests in this class
System.out.println("Tests completed");
}
}

TestNG Annotations Reference

AnnotationDescription
@BeforeSuiteBefore all tests in suite
@BeforeClassBefore first test in class
@BeforeMethodBefore each test method
@TestMarks test method
@AfterMethodAfter each test method
@AfterClassAfter last test in class
@AfterSuiteAfter all tests in suite

TestNG Data Providers

import org.testng.annotations.DataProvider;
public class DataDrivenTest {
private WebDriver driver;
@BeforeMethod
public void setup() {
driver = new ChromeDriver();
}
@DataProvider(name = "loginData")
public Object[][] loginDataProvider() {
return new Object[][] {
{"user1", "pass1", true},
{"user2", "pass2", true},
{"invalid", "wrong", false}
};
}
@Test(dataProvider = "loginData")
public void testLogin(String username, String password, boolean shouldPass) {
driver.get("https://example.com/login");
driver.findElement(By.id("username")).sendKeys(username);
driver.findElement(By.id("password")).sendKeys(password);
driver.findElement(By.id("login")).click();
if (shouldPass) {
Assert.assertTrue(driver.getCurrentUrl().contains("/dashboard"));
} else {
Assert.assertTrue(driver.findElement(By.className("error")).isDisplayed());
}
}
@AfterMethod
public void teardown() {
driver.quit();
}
}

TestNG XML Suite

testng.xml
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Regression Suite" parallel="methods" thread-count="3">
<test name="Login Tests">
<classes>
<class name="tests.LoginTest"/>
<class name="tests.RegistrationTest"/>
</classes>
</test>
<test name="Search Tests">
<classes>
<class name="tests.SearchTest"/>
</classes>
</test>
</suite>

JUnit 5 Setup

Maven Dependencies

<dependencies>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.18.1</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
</dependencies>

Basic JUnit 5 Test

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.By;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
class LoginTest {
private WebDriver driver;
@BeforeAll
static void setupAll() {
// Runs once before all tests
System.out.println("Setting up test class");
}
@BeforeEach
void setup() {
// Runs before each test
driver = new ChromeDriver();
driver.manage().window().maximize();
}
@Test
@DisplayName("Valid user can login successfully")
void testValidLogin() {
driver.get("https://example.com/login");
driver.findElement(By.id("username")).sendKeys("validuser");
driver.findElement(By.id("password")).sendKeys("validpass");
driver.findElement(By.id("login")).click();
assertTrue(driver.getCurrentUrl().contains("/dashboard"),
"Should redirect to dashboard");
}
@Test
@DisplayName("Invalid credentials show error")
void testInvalidLogin() {
driver.get("https://example.com/login");
driver.findElement(By.id("username")).sendKeys("invalid");
driver.findElement(By.id("password")).sendKeys("wrong");
driver.findElement(By.id("login")).click();
String error = driver.findElement(By.className("error")).getText();
assertEquals("Invalid credentials", error);
}
@AfterEach
void teardown() {
if (driver != null) {
driver.quit();
}
}
@AfterAll
static void teardownAll() {
System.out.println("Tests completed");
}
}

JUnit 5 Annotations Reference

AnnotationDescription
@BeforeAllBefore all tests (static)
@BeforeEachBefore each test
@TestMarks test method
@AfterEachAfter each test
@AfterAllAfter all tests (static)
@DisplayNameCustom test name
@DisabledSkip test

JUnit 5 Parameterized Tests

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.*;
class ParameterizedLoginTest {
private WebDriver driver;
@BeforeEach
void setup() {
driver = new ChromeDriver();
}
@ParameterizedTest
@CsvSource({
"user1, pass1, true",
"user2, pass2, true",
"invalid, wrong, false"
})
void testLogin(String username, String password, boolean shouldPass) {
driver.get("https://example.com/login");
driver.findElement(By.id("username")).sendKeys(username);
driver.findElement(By.id("password")).sendKeys(password);
driver.findElement(By.id("login")).click();
if (shouldPass) {
assertTrue(driver.getCurrentUrl().contains("/dashboard"));
} else {
assertTrue(driver.findElement(By.className("error")).isDisplayed());
}
}
@ParameterizedTest
@ValueSource(strings = {"chrome", "firefox", "edge"})
void testBrowsers(String browser) {
// Test across browsers
}
@AfterEach
void teardown() {
driver.quit();
}
}

Base Test Class Pattern

// For TestNG
public class BaseTest {
protected WebDriver driver;
protected WebDriverWait wait;
@BeforeMethod
public void setup() {
driver = DriverFactory.getDriver();
wait = new WebDriverWait(driver, Duration.ofSeconds(10));
driver.manage().window().maximize();
}
@AfterMethod
public void teardown(ITestResult result) {
if (result.getStatus() == ITestResult.FAILURE) {
takeScreenshot(result.getName());
}
DriverFactory.quitDriver();
}
protected void takeScreenshot(String name) {
// Screenshot logic
}
}
// Tests extend BaseTest
public class LoginTest extends BaseTest {
@Test
public void testLogin() {
driver.get("https://example.com");
// Test logic using inherited driver
}
}

TestNG vs JUnit 5

FeatureTestNGJUnit 5
Parallel executionBuilt-in XML configJupiter parallel config
Data providers@DataProvider@ParameterizedTest
Test groups@Test(groups="")@Tag
DependenciesdependsOnMethods@Order
Suite filesXML-basedProgrammatic
ReportingBuilt-in HTMLRequires plugins

Next Steps

Copy-paste recipes for this topic

Related lessons