TestNG is a powerful testing framework for Java that offers advanced features for test organization, execution, and reporting. This module covers the integration of WebDriverIO with TestNG, allowing you to leverage TestNG's capabilities while using WebDriverIO for browser automation.
By the end of this module, you will be able to:
TestNG offers several advantages when integrated with WebDriverIO:
Comparing TestNG with other frameworks commonly used with WebDriverIO:
Feature | TestNG | Mocha | Jasmine | Cucumber |
---|---|---|---|---|
Language | Java | JavaScript | JavaScript | Gherkin/JavaScript |
Annotations | Yes | No | No | No |
Data-Driven Testing | Built-in | Limited | Limited | Scenario Outlines |
Parallel Execution | Built-in | Limited | Limited | Requires configuration |
Dependency Management | Yes | No | No | No |
Grouping | Yes | Limited | Limited | Tags |
Reporting | Built-in | Requires plugins | Requires plugins | Requires plugins |
The integration architecture typically involves:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ TestNG │────▶│ Java WebDriverIO │────▶│ Selenium │────▶│ Browser │
│ Framework │ │ Bindings │ │ Server │ │ │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
Before setting up WebDriverIO with TestNG, ensure you have:
Create a Maven project with the following pom.xml
:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>webdriverio-testng</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<testng.version>7.4.0</testng.version>
<selenium.version>4.1.0</selenium.version>
<webdrivermanager.version>5.0.3</webdrivermanager.version>
</properties>
<dependencies>
<!-- TestNG -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
<!-- Selenium WebDriver -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
</dependency>
<!-- WebDriverManager -->
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>${webdrivermanager.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
</plugins>
</build>
</project>
Alternatively, create a Gradle project with the following build.gradle
:
plugins {
id 'java'
}
group 'com.example'
version '1.0-SNAPSHOT'
sourceCompatibility = 11
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.testng:testng:7.4.0'
implementation 'org.seleniumhq.selenium:selenium-java:4.1.0'
implementation 'io.github.bonigarcia:webdrivermanager:5.0.3'
}
test {
useTestNG() {
suites 'src/test/resources/testng.xml'
}
}
Install WebDriverIO and required dependencies:
npm init -y
npm install @wdio/cli --save-dev
npx wdio config
During the configuration wizard, select:
Create a wdio.conf.js
file:
exports.config = {
runner: 'local',
specs: [
'./src/test/java/**/*.java'
],
exclude: [],
maxInstances: 10,
capabilities: [{
maxInstances: 5,
browserName: 'chrome',
acceptInsecureCerts: true
}],
logLevel: 'info',
bail: 0,
baseUrl: 'http://localhost',
waitforTimeout: 10000,
connectionRetryTimeout: 120000,
connectionRetryCount: 3,
framework: 'testng',
reporters: ['spec'],
services: ['chromedriver'],
testng: {
// TestNG specific configuration
suiteXmlFiles: ['./testng.xml']
}
};
Create a testng.xml
file in the project root or in src/test/resources
:
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="WebDriverIO TestNG Suite" verbose="1">
<test name="WebDriverIO Tests">
<classes>
<class name="com.example.tests.LoginTest"/>
<class name="com.example.tests.HomePageTest"/>
</classes>
</test>
</suite>
TestNG provides annotations to control test execution flow:
package com.example.tests;
import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.*;
public class BasicTest {
protected WebDriver driver;
@BeforeSuite
public void beforeSuite() {
System.out.println("Before Suite: Setting up WebDriverManager");
WebDriverManager.chromedriver().setup();
}
@BeforeClass
public void beforeClass() {
System.out.println("Before Class: Initializing test class");
}
@BeforeMethod
public void beforeMethod() {
System.out.println("Before Method: Setting up WebDriver");
driver = new ChromeDriver();
driver.manage().window().maximize();
}
@Test
public void testExample() {
System.out.println("Test: Executing test");
driver.get("https://example.com");
assert driver.getTitle().contains("Example");
}
@AfterMethod
public void afterMethod() {
System.out.println("After Method: Closing WebDriver");
if (driver != null) {
driver.quit();
}
}
@AfterClass
public void afterClass() {
System.out.println("After Class: Cleaning up test class");
}
@AfterSuite
public void afterSuite() {
System.out.println("After Suite: Final cleanup");
}
}
TestNG executes annotations in the following order:
@BeforeSuite
@BeforeTest
@BeforeClass
@BeforeGroups
@BeforeMethod
@Test
@AfterMethod
@AfterGroups
@AfterClass
@AfterTest
@AfterSuite
Integrate WebDriverIO commands within TestNG annotations:
package com.example.tests;
import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Assert;
import org.testng.annotations.*;
import java.time.Duration;
public class LoginTest {
protected WebDriver driver;
protected WebDriverWait wait;
@BeforeSuite
public void setupWebDriverManager() {
WebDriverManager.chromedriver().setup();
}
@BeforeMethod
public void setupWebDriver() {
driver = new ChromeDriver();
driver.manage().window().maximize();
wait = new WebDriverWait(driver, Duration.ofSeconds(10));
}
@Test
public void testLogin() {
// Navigate to the login page
driver.get("https://example.com/login");
// Find and interact with elements
WebElement usernameField = wait.until(
ExpectedConditions.visibilityOfElementLocated(By.id("username"))
);
usernameField.sendKeys("testuser");
WebElement passwordField = driver.findElement(By.id("password"));
passwordField.sendKeys("password123");
WebElement loginButton = driver.findElement(By.id("login-button"));
loginButton.click();
// Wait for successful login
WebElement welcomeMessage = wait.until(
ExpectedConditions.visibilityOfElementLocated(By.className("welcome-message"))
);
// Assert the welcome message
Assert.assertTrue(welcomeMessage.getText().contains("Welcome, Test User"));
}
@AfterMethod
public void tearDown() {
if (driver != null) {
driver.quit();
}
}
}
Organize tests into logical groups:
package com.example.tests;
import org.testng.annotations.Test;
public class GroupedTests {
@Test(groups = {"smoke"})
public void smokeTest1() {
// Test implementation
}
@Test(groups = {"smoke"})
public void smokeTest2() {
// Test implementation
}
@Test(groups = {"regression"})
public void regressionTest1() {
// Test implementation
}
@Test(groups = {"regression", "critical"})
public void criticalRegressionTest() {
// Test implementation
}
}
Configure groups in testng.xml
:
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Grouped Tests Suite">
<test name="Smoke Tests">
<groups>
<run>
<include name="smoke"/>
</run>
</groups>
<classes>
<class name="com.example.tests.GroupedTests"/>
</classes>
</test>
<test name="Regression Tests">
<groups>
<run>
<include name="regression"/>
</run>
</groups>
<classes>
<class name="com.example.tests.GroupedTests"/>
</classes>
</test>
</suite>
Define dependencies between tests:
package com.example.tests;
import org.testng.annotations.Test;
public class DependentTests {
@Test(groups = {"setup"})
public void setupEnvironment() {
// Set up test environment
System.out.println("Setting up test environment");
}
@Test(dependsOnMethods = {"setupEnvironment"})
public void testFeature1() {
// Test feature 1
System.out.println("Testing feature 1");
}
@Test(dependsOnMethods = {"setupEnvironment"})
public void testFeature2() {
// Test feature 2
System.out.println("Testing feature 2");
}
@Test(dependsOnMethods = {"testFeature1", "testFeature2"})
public void testIntegration() {
// Test integration
System.out.println("Testing integration");
}
}
Use TestNG's data provider for parameterized tests:
package com.example.tests;
import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.annotations.*;
public class DataDrivenLoginTest {
private WebDriver driver;
@BeforeClass
public void setupClass() {
WebDriverManager.chromedriver().setup();
}
@BeforeMethod
public void setupMethod() {
driver = new ChromeDriver();
driver.manage().window().maximize();
}
@DataProvider(name = "loginData")
public Object[][] loginData() {
return new Object[][] {
{"validuser", "validpass", true, "Welcome, Valid User"},
{"invaliduser", "invalidpass", false, "Invalid credentials"},
{"validuser", "invalidpass", false, "Invalid credentials"},
{"", "", false, "Username and password are required"}
};
}
@Test(dataProvider = "loginData")
public void testLogin(String username, String password, boolean shouldSucceed, String expectedMessage) {
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-button")).click();
if (shouldSucceed) {
String welcomeMessage = driver.findElement(By.className("welcome-message")).getText();
Assert.assertEquals(welcomeMessage, expectedMessage);
} else {
String errorMessage = driver.findElement(By.className("error-message")).getText();
Assert.assertEquals(errorMessage, expectedMessage);
}
}
@AfterMethod
public void tearDown() {
if (driver != null) {
driver.quit();
}
}
}
Configure parallel execution in testng.xml
:
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Parallel Tests Suite" parallel="methods" thread-count="4">
<test name="Parallel Tests">
<classes>
<class name="com.example.tests.ParallelTest"/>
</classes>
</test>
</suite>
Implement tests that can run in parallel:
```java package com.example.tests;
import io.github.bonigarcia.wdm.WebDriverManager; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.testng.Assert; import org.testng.annotations.*;
public class ParallelTest {
@BeforeClass
public static void setupClass() {
WebDriverManager.chromedriver().setup();
}
@Test
public void testCompleteWorkflow() {
// Complete test implementation
loginPage.login("user@example.com", "password");
dashboardPage.navigateToReports();
reportsPage.generateReport("monthly");
Assert.assertTrue(reportsPage.isReportGenerated());
}
This module covered the integration of WebDriverIO with TestNG, providing you with the tools to create robust, organized, and maintainable test suites. TestNG's advanced features complement WebDriverIO's browser automation capabilities perfectly.
Key takeaways:
In the next module, we'll explore real-world project implementation, applying all the concepts learned throughout the course.