TestNG and JUnit Integration
Integrate Selenium with TestNG and JUnit test frameworks for structured, maintainable Java tests.
Selenium 3 & 4 Stable
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
| Annotation | Description |
|---|---|
@BeforeSuite | Before all tests in suite |
@BeforeClass | Before first test in class |
@BeforeMethod | Before each test method |
@Test | Marks test method |
@AfterMethod | After each test method |
@AfterClass | After last test in class |
@AfterSuite | After 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
<!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
| Annotation | Description |
|---|---|
@BeforeAll | Before all tests (static) |
@BeforeEach | Before each test |
@Test | Marks test method |
@AfterEach | After each test |
@AfterAll | After all tests (static) |
@DisplayName | Custom test name |
@Disabled | Skip 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 TestNGpublic 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 BaseTestpublic class LoginTest extends BaseTest { @Test public void testLogin() { driver.get("https://example.com"); // Test logic using inherited driver }}TestNG vs JUnit 5
| Feature | TestNG | JUnit 5 |
|---|---|---|
| Parallel execution | Built-in XML config | Jupiter parallel config |
| Data providers | @DataProvider | @ParameterizedTest |
| Test groups | @Test(groups="") | @Tag |
| Dependencies | dependsOnMethods | @Order |
| Suite files | XML-based | Programmatic |
| Reporting | Built-in HTML | Requires plugins |
Next Steps
- Page Object Model - Structure your tests
- Parallel Execution - Run tests concurrently
- Data-Driven Testing - External test data
- pytest Integration - Python framework comparison