ExtentReport, one of the most popular and extensively used test reports, has been officially retired due to changes in automated testing techniques. The same developer, Anshoo Arora, fills the void with ChainTest, a potent reporting system. With its enhanced capabilities, real-time data, and seamless integration options, ChainTest is anticipated to transform how we see test results.
Table of Contents
- What is ChainTest Report?
- Key Features of ChainTest Report
- Prerequisite
- Dependency List
- Structure of Project
- Implementation
What is ChainTest Report?
ChainTest is a complete reporting system, it supports a variety of sources, including email, static data, and real-time, historical analytics with ChainLP. The next-generation reporting framework ChainTest was developed for a number of test frameworks, such as Cucumber, JUnit, and TestNG.
Key Features of ChainTest Report
Combination of Report – ChainTest generates a combination of static report, email and dynamic report.
Real Time Analysis – ChainTest’s incorporation of real-time statistics allows teams to monitor test executions in real time. This feature is very helpful for extensive testing projects with tight deadlines.
Simple SetUp – Setting up ChainTest is simple and quick with an easily accessible properties file. The setup procedure is streamlined by the ease with which users can specify project names, server URLs, and report options.
Support multiple test framework – This is compatible with a number of test frameworks, such as Cucumber, JUnit, and TestNG. Integration with PyTest is also expected to be available soon.
Docker-Enabled Setup – Docker simplifies the process of setting up ChainTest. The Chain LP server can be spun up in a matter of minutes by users by pulling the Docker image, which streamlines deployment and minimizes system requirements.
Monitoring of Historical Data – The historical data tracking tool, which enables teams to examine performance trends across several builds, is one notable feature. This makes it easier to spot trends and enhance test-taking techniques.
Prerequisite
- Java 17 is installed
- Maven is installed
- Eclipse or IntelliJ is installed
Dependency List:
- Selenium – 4.28.1
- Java 17
- TestNG – 7.10.2
- Maven – 3.9.6
- ChainTest TestNG – 1.0.7
Structure of Project

Implementation
Add ChainTest TestNG plugin dependency in Maven Project.
<dependency>
<groupId>com.aventstack</groupId>
<artifactId>chaintest-testng</artifactId>
<version>${chaintest.testng.version}</version>
</dependency>
The complete pom.xml looks like as shown below:
<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>ChainTest_TestNG_Demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>ChainTest_TestNG_Demo</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<selenium.version>4.28.1</selenium.version>
<testng.version>7.10.2</testng.version>
<chaintest.testng.version>1.0.7</chaintest.testng.version>
<maven.compiler.plugin.version>3.13.0</maven.compiler.plugin.version>
<maven.compiler.source.version>17</maven.compiler.source.version>
<maven.compiler.target.version>17</maven.compiler.target.version>
<maven.surefire.plugin.version>3.2.5</maven.surefire.plugin.version>
<aspectj.version>1.9.20.1</aspectj.version>
</properties>
<dependencies>
<!-- Selenium -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
</dependency>
<!-- TestNG -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
<!-- Chain Test Report -->
<dependency>
<groupId>com.aventstack</groupId>
<artifactId>chaintest-testng</artifactId>
<version>${chaintest.testng.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.plugin.version}</version>
<configuration>
<source>${maven.compiler.source.version}</source>
<target>${maven.compiler.target.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven.surefire.plugin.version}</version>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
</plugins>
</build>
</project>
Create Pages and Test Code for the pages
BasePage
package com.example;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.PageFactory;
public class BasePage {
public WebDriver driver;
public BasePage(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver,this);
}
}
LoginPage
package com.example;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class LoginPage extends BasePage {
public LoginPage(WebDriver driver) {
super(driver);
}
@FindBy(name = "username")
public WebElement userName;
@FindBy(name = "password")
public WebElement password;
@FindBy(xpath = "//*[@type='submit']")
public WebElement loginBtn;
@FindBy(xpath = "//*[@id='flash']")
public WebElement errorMessage;
public void login(String strUserName, String strPassword) {
userName.sendKeys(strUserName);
password.sendKeys(strPassword);
loginBtn.click();
}
public String getErrorMessage() {
return errorMessage.getText();
}
}
SecurePage
package com.example;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class SecurePage extends BasePage {
public SecurePage(WebDriver driver) {
super(driver);
}
@FindBy(xpath = "//*[@id='flash']")
public WebElement securePageTitle;
public String getSecurePageTitle() {
return securePageTitle.getText();
}
}
BaseTests
package com.example;
import com.aventstack.chaintest.plugins.ChainTestListener;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.edge.EdgeOptions;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.testng.ITestResult;
import org.testng.annotations.*;
import java.time.Duration;
@Listeners(ChainTestListener.class)
public class BaseTests {
public static WebDriver driver;
public final static int TIMEOUT = 10;
@BeforeTest
@Parameters("browserName")
public void setup(String browserName) throws Exception {
System.out.println("Browser : " + browserName);
switch (browserName.toLowerCase().trim()) {
case "chrome":
ChromeOptions options = new ChromeOptions();
options.addArguments("--remote-allow-origins=*");
options.addArguments("--no-sandbox");
options.addArguments("--disable-dev-shm-usage");
driver = new ChromeDriver(options);
break;
case "firefox":
FirefoxOptions firefoxOptions = new FirefoxOptions();
driver = new FirefoxDriver(firefoxOptions);
break;
case ("edge"):
EdgeOptions edgeOptions = new EdgeOptions();
driver = new EdgeDriver(edgeOptions);
break;
default:
System.out.println("Incorrect browser is supplied...." + browserName);
throw new IllegalArgumentException("Wrong Browser provided: " + browserName);
}
driver.manage().window().maximize();
driver.get("https://the-internet.herokuapp.com/login");
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(TIMEOUT));
}
@AfterTest
public void tearDown() {
driver.quit();
}
}
LoginPageTests
package com.example;
import org.testng.Assert;
import org.testng.annotations.Test;
public class LoginPageTests extends BaseTests {
String actualLoginPageTitle;
String actualErrorMessage;
String actualDashboardPageTitle;
@Test(priority = 0)
public void verifyPageTitle() {
actualLoginPageTitle = driver.getTitle();
// Verify Page Title - Failed Test
Assert.assertEquals(actualLoginPageTitle, "The Internet !!");
}
@Test(priority = 1)
public void invalidCredentials() {
LoginPage loginPage = new LoginPage(driver);
loginPage.login("tomsmith", "happy!");
actualErrorMessage = loginPage.getErrorMessage();
// Verify Error Message
Assert.assertTrue(actualErrorMessage.contains("Your password is invalid!"));
}
@Test(priority = 2)
public void validLogin() {
LoginPage loginPage = new LoginPage(driver);
loginPage.login("tomsmith", "SuperSecretPassword!");
SecurePage securePage = new SecurePage(driver);
actualSecurePageTitle = securePage.getSecurePageTitle();
// Verify Home Page
Assert.assertTrue(actualSecurePageTitle.contains("You logged into a secure area!"));
}
}
Add chaintest.properties
Add chaintest.properties to the classpath, example: src/test/resources/chaintest.properties.
# chaintest configuration
chaintest.project.name= ChaninTest Report with Selenium and TestNG
# generators:
## chainlp
chaintest.generator.chainlp.enabled=true
chaintest.generator.chainlp.class-name=com.aventstack.chaintest.generator.ChainLPGenerator
chaintest.generator.chainlp.host.url=http://localhost/
chaintest.generator.chainlp.client.request-timeout-s=30
chaintest.generator.chainlp.client.expect-continue=false
chaintest.generator.chainlp.client.max-retries=3
## simple
chaintest.generator.simple.enabled=true
chaintest.generator.simple.document-title=chaintest
chaintest.generator.simple.class-name=com.aventstack.chaintest.generator.ChainTestSimpleGenerator
chaintest.generator.simple.output-file=target/chaintest/Index.html
chaintest.generator.simple.offline=false
chaintest.generator.simple.dark-theme=true
chaintest.generator.simple.datetime-format=yyyy-MM-dd hh:mm:ss a
chaintest.generator.simple.js=
chaintest.generator.simple.css=
## email
chaintest.generator.email.enabled=true
chaintest.generator.email.class-name=com.aventstack.chaintest.generator.ChainTestEmailGenerator
chaintest.generator.email.output-file=target/chaintest/Email.html
chaintest.generator.email.datetime-format=yyyy-MM-dd hh:mm:ss a
Add ChainTestListener
Add ChainTestListener in the list of @Listeners of the test class. Here, I have added this to BaseTests class.
@Listeners(ChainTestListener.class)
public class BaseTests {
}
Create a testng.xml at the root directory.
Create a testng.xml at the root of the project. This xml contains parameter detail like allow passing parameters to test methods ie browser name.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="ChainTestReport with TestNG">
<test name="Chrome Test">
<parameter name="browserName" value="chrome" />
<classes>
<class name="com.example.LoginPageTests"/>
</classes>
</test> <!-- Test -->
<test name="Firefox Test">
<parameter name="browserName" value="firefox" />
<classes>
<class name="com.example.LoginPageTests"/>
</classes>
</test> <!-- Test -->
<test name="Edge Test">
<parameter name="browserName" value="edge" />
<classes>
<class name="com.example.LoginPageTests"/>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
Run the tests from testng.xml.
Right click on testng.xml and select Run ‘…\testng.xml’

The execution status looks like as shown below. There are 3 tests and 1 test will fail. These 3 tests are executed in 3 different browsers – Chrome, Firefox and Edge. So, total we are executing 9 tests and out of them 6 passed and 3 failed.

View ChainTest Reports
ChainTest Reports are generated in target folder – Index.html and email.html.

Index.html
Right-click and open with Web Browser.
The index.html has a summary section that displays the summary of the execution. The summary includes the overview of the pass/fail using a pictogram, start time, end time, and pass/fail details of tests as shown in the image below.

Email.html
This report contains the high level summary of the test execution.

Run the tests through command line
To run the tests, use the below command
mvn clean test

We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!