Previous: Custom Commands and Utilities Next: Real-World Project Implementation

Module 10: TestNG Integration

Overview

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.

Learning Objectives

By the end of this module, you will be able to:

  1. Set up a WebDriverIO project with TestNG integration
  2. Implement TestNG annotations and features in WebDriverIO tests
  3. Create data-driven tests using TestNG data providers
  4. Configure parallel test execution with TestNG
  5. Generate and customize TestNG reports

1. Introduction to TestNG with WebDriverIO

1.1 Why Integrate TestNG with WebDriverIO?

TestNG offers several advantages when integrated with WebDriverIO:

  1. Advanced Test Organization: TestNG provides a robust framework for organizing tests into suites, groups, and dependencies.
  2. Powerful Annotations: TestNG's annotation system offers fine-grained control over test execution flow.
  3. Data-Driven Testing: TestNG's data provider mechanism simplifies parameterized testing.
  4. Parallel Execution: TestNG has built-in support for parallel test execution.
  5. Comprehensive Reporting: TestNG generates detailed HTML reports and can be integrated with other reporting tools.

1.2 TestNG vs. Other Testing Frameworks

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

1.3 Architecture Overview

The integration architecture typically involves:

  1. WebDriverIO: Handles browser automation and element interactions
  2. TestNG: Manages test execution, organization, and reporting
  3. Java Bindings: Connect WebDriverIO (JavaScript) with TestNG (Java)
  4. Selenium Server: Acts as a bridge between WebDriverIO and browsers
┌─────────────┐     ┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   TestNG    │────▶│ Java WebDriverIO │────▶│ Selenium   │────▶│  Browser   │
│  Framework  │     │   Bindings   │     │   Server    │     │            │
└─────────────┘     └─────────────┘     └─────────────┘     └─────────────┘

2. Setting Up WebDriverIO with TestNG

2.1 Prerequisites

Before setting up WebDriverIO with TestNG, ensure you have:

  1. Java Development Kit (JDK) 8 or higher
  2. Maven or Gradle for dependency management
  3. Node.js and npm (for WebDriverIO)
  4. An IDE like IntelliJ IDEA or Eclipse

2.2 Project Setup

2.2.1 Maven Project Setup

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>

2.2.2 Gradle Project Setup

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'
    }
}

2.3 WebDriverIO Setup

2.3.1 WebDriverIO Installation

Install WebDriverIO and required dependencies:

npm init -y
npm install @wdio/cli --save-dev
npx wdio config

During the configuration wizard, select:

2.3.2 WebDriverIO Configuration

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']
    }
};

2.4 TestNG Configuration

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>

3. TestNG Annotations with WebDriverIO

3.1 Basic TestNG Annotations

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");
    }
}

3.2 TestNG Annotation Hierarchy

TestNG executes annotations in the following order:

  1. @BeforeSuite
  2. @BeforeTest
  3. @BeforeClass
  4. @BeforeGroups
  5. @BeforeMethod
  6. @Test
  7. @AfterMethod
  8. @AfterGroups
  9. @AfterClass
  10. @AfterTest
  11. @AfterSuite

3.3 WebDriverIO Integration with TestNG Annotations

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

4. Advanced TestNG Features with WebDriverIO

4.1 Test Groups

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>

4.2 Test Dependencies

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");
    }
}

4.3 Data-Driven Testing with TestNG

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

4.4 Parallel Test Execution

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

Summary

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:

  1. TestNG provides powerful test organization and execution features
  2. Data-driven testing with TestNG data providers enables comprehensive test coverage
  3. TestNG's reporting capabilities offer detailed insights into test execution
  4. Parallel execution with TestNG significantly improves test performance
  5. Integration with CI/CD pipelines ensures continuous quality assurance

In the next module, we'll explore real-world project implementation, applying all the concepts learned throughout the course.

Previous: Custom Commands and Utilities Next: Real-World Project Implementation