We can create beautiful, interactive, and detailed reports for your tests using the Extent Reports library. Add events, screenshots, tags, devices, authors, or any other relevant information you deem necessary to create a descriptive and visually appealing report that you have complete control over.
ExtentReport
ExtentReports Version 4 for Cucumber 5, Selenium, and TestNG
The previous tutorial explained the generation of Allure Report with Cucumber5, Selenium and TestNG in a Maven project. In this tutorial, I will explain the steps to create an Extent Report4 with Cucumber5, Selenium, and TestNG in a Maven project.
Pre-Requisite:
- Java 8 or higher is needed for ExtentReport5
- Maven
- JAVA IDE (like Eclipse, IntelliJ, or so on)
- TestNG installed
- Cucumber Eclipse plugin (in case using Eclipse)
Project Structure

Step 1 – Add Maven dependencies to the POM
Add ExtentReport dependency.
<!-- Extent Report -->
<dependency>
<groupId>com.aventstack</groupId>
<artifactId>extentreports</artifactId>
<version>${extentreports.version}</version>
</dependency>
Add tech grasshopper maven dependency for Cucumber. The below version of extentreports-cucumber5-adapter dependency needs to be added to the POM, to work with ExtentReports version 4.
<!-- Cucumber ExtentReport Adapter -->
<dependency>
<groupId>tech.grasshopper</groupId>
<artifactId>extentreports-cucumber5-adapter</artifactId>
<version>1.51</version>
</dependency>
If you want to use ExtentReport Version5, then use version – 2.13.0.
The complete POM.xml will look as shown below with other Selenium and TestNG dependencies.
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<cucumber.version>5.7.0</cucumber.version>
<extentreports.cucumber5.adapter.version>1.5.1</extentreports.cucumber5.adapter.version>
<extentreports.version>4.1.7</extentreports.version>
<selenium.version>3.141.59</selenium.version>
<webdrivermanager.version>5.2.1</webdrivermanager.version>
<testng.version>6.14.3</testng.version>
<apache.common.version>2.4</apache.common.version>
<maven.compiler.plugin.version>3.7.0</maven.compiler.plugin.version>
<maven.surefire.plugin.version>3.0.0-M5</maven.surefire.plugin.version>
<maven.compiler.source.version>11</maven.compiler.source.version>
<maven.compiler.target.version>11</maven.compiler.target.version>
</properties>
<dependencies>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>${cucumber.version}</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-testng</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>
<!-- Cucumber ExtentReport Adapter -->
<dependency>
<groupId>tech.grasshopper</groupId>
<artifactId>extentreports-cucumber5-adapter</artifactId>
<version>${extentreports.cucumber5.adapter.version}</version>
</dependency>
<!-- Extent Report -->
<dependency>
<groupId>com.aventstack</groupId>
<artifactId>extentreports</artifactId>
<version>${extentreports.version}</version>
</dependency>
<!-- Selenium -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
</dependency>
<!-- Web Driver Manager -->
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>${webdrivermanager.version}</version>
</dependency>
<!-- TestNG -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
<!-- Apache Common -->
<dependency>
<groupId>org.apache.directory.studio</groupId>
<artifactId>org.apache.commons.io</artifactId>
<version>${apache.common.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>
Step 2: Create a feature file in src/test/resources
Below is a sample feature file. I have added 2 failed scenarios – @FaceBookLink(Invalid XPath) and @MissingUsername (Incorrect Verification).
Feature: Login to HRM Application
Background:
Given User is on HRMLogin page "https://opensource-demo.orangehrmlive.com/"
@ValidCredentials
Scenario: Login with valid credentials
When User enters username as "Admin" and password as "admin123"
Then User should be able to login sucessfully and new page opens
@InvalidCredentials
Scenario Outline: Login with invalid credentials
When User enters username as "<username>" and password as "<password>"
Then User should be able to see error message "<errorMessage>"
Examples:
| username | password | errorMessage |
| Admin | admin12$$ | Invalid credentials |
| admin$$ | admin123 | Invalid credentials |
| admin123 | Admin | Invalid credentials |
| $$$$$$$ | &&&&&&&& | Invalid credentials |
@FaceBookLink
Scenario: Verfy FaceBook Icon on Login Page
Then User should be able to see FaceBook Icon
@MissingUsername
Scenario: Verify error message when username is missing
When User enters username as "" and password as "admin123"
Then User should be able to see error message for empty username as "Empty Username"
Step 3: Create extent.properties file in src/test/resources
We need to create the extent.properties file in the src/test/resources folder for the grasshopper extent report adapter to recognize it. Using a property file for reporting is quite helpful if you want to define several different properties.
Let’s enable spark report in an extent properties file:
extent.reporter.avent.start=false
extent.reporter.bdd.start=false
extent.reporter.cards.start=false
extent.reporter.email.start=false
extent.reporter.html.start=true
extent.reporter.klov.start=false
extent.reporter.logger.start=true
extent.reporter.tabular.start=false
extent.reporter.avent.config=
extent.reporter.bdd.config=
extent.reporter.cards.config=
extent.reporter.email.config=
extent.reporter.html.config=
extent.reporter.klov.config=
extent.reporter.logger.config=
extent.reporter.tabular.config=
extent.reporter.avent.out=Reports/AventReport/
extent.reporter.bdd.out=Reports/BddReport/
extent.reporter.cards.out=Reports/CardsReport/
extent.reporter.email.out=Reports/EmailReport/ExtentEmail.html
extent.reporter.html.out=Reports/HtmlReport/ExtentHtml.html
extent.reporter.logger.out=Reports/LoggerReport/
extent.reporter.tabular.out=Reports/TabularReport/
#Screenshot
screenshot.dir=Reports/Screenshots/
screenshot.rel.path=../Screenshots/

Step 4: Create a Helper class in src/main/java
We have used Page Object Model with Cucumber and TestNG.
Create a Helper class where we are initializing the web driver, initializing the web driver wait, defining the timeouts, and creating a private constructor of the class, it will declare the web driver, so whenever we create an object of this class, a new web browser is invoked. We are using a setter and getter method to get the object of Chromedriver with the help of a private constructor itself within the same class.
HelperClass
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import io.github.bonigarcia.wdm.WebDriverManager;
public class HelperClass {
private static HelperClass helperClass;
private static WebDriver driver;
public final static int TIMEOUT = 10;
private HelperClass() {
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
driver.manage().timeouts().implicitlyWait(TIMEOUT,TimeUnit.SECONDS);
driver.manage().window().maximize();
}
public static void openPage(String url) {
driver.get(url);
}
public static WebDriver getDriver() {
return driver;
}
public static void setUpDriver() {
if (helperClass==null) {
helperClass = new HelperClass();
}
}
public static void tearDown() {
if(driver!=null) {
driver.close();
driver.quit();
}
helperClass = null;
}
}
Step 5: Create Locator classes in src/main/java
Create a locator class for each page that contains the detail of the locators of all the web elements. Here, I’m creating 2 locator classes – LoginPageLocators and HomePageLocators.
LoginPageLocators
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class LoginPageLocators {
@FindBy(name = "txtUsername")
public WebElement userName;
@FindBy(name = "txtPassword")
public WebElement password;
@FindBy(id = "logInPanelHeading")
public WebElement titleText;
@FindBy(id = "btnLogin")
public WebElement login;
@FindBy(id = "spanMessage")
public WebElement errorMessage;
@FindBy(xpath = "//*[@id='social-icons']/a[1]/img")
public WebElement linkedInIcon;
@FindBy(xpath = "//*[@id='social-icons']/a[6]/img") //Invalid Xpath
public WebElement faceBookIcon;
}
HomePageLocators
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class HomePageLocators {
@FindBy(xpath = "//*[@id='app']/div[1]/div[2]/div[2]/div/div[1]/div[1]/div[1]/h5")
public WebElement homePageUserName;
}
Step 6: Create Action classes in src/main/java
Create the action classes for each web page. These action classes contain all the methods needed by the step definitions. In this case, I have created 2 action classes – LoginPageActions and HomePageActions
LoginPageActions
In this class, the very first thing will do is to create the object of the LoginPageLocators class so that we should be able to access all the PageFactory elements. Secondly, create a public constructor of LoginPageActions class.
package com.example.testng.actions;
import org.openqa.selenium.support.PageFactory;
import com.example.testng.locators.LoginPageLocators;
import com.example.testng.utils.HelperClass;
public class LoginPageActions {
LoginPageLocators loginPageLocators = null;
public LoginPageActions() {
this.loginPageLocators = new LoginPageLocators();
PageFactory.initElements(HelperClass.getDriver(),loginPageLocators);
}
public void login(String strUserName, String strPassword) {
// Fill user name
loginPageLocators.userName.sendKeys(strUserName);
// Fill password
loginPageLocators.password.sendKeys(strPassword);
// Click Login button
loginPageLocators.login.click();
}
//Get the title of Login Page")
public String getLoginTitle() {
return loginPageLocators.titleText.getText();
}
// Get the error message of Login Page
public String getErrorMessage() {
return loginPageLocators.errorMessage.getText();
}
// FaceBook Icon is displayed
public Boolean getFaceBookIcon() {
return loginPageLocators.faceBookIcon.isDisplayed();
}
// Get the error message when username is blank
public String getMissingUsernameText() {
return loginPageLocators.missingUsernameErrorMessage.getText();
}
}
HomePageActions
import org.openqa.selenium.support.PageFactory;
import com.example.testng.locators.HomePageLocators;
import com.example.testng.utils.HelperClass;
public class HomePageActions {
HomePageLocators homePageLocators = null;
public HomePageActions() {
this.homePageLocators = new HomePageLocators();
PageFactory.initElements(HelperClass.getDriver(),homePageLocators);
}
// Get the User name from Home Page
public String getHomePageText() {
return homePageLocators.homePageUserName.getText();
}
}
Step 7: Create a Step Definition file in src/test/java
Create the corresponding Step Definition file of the feature file.
LoginPageDefinitions
import org.testng.Assert;
import com.example.testng.actions.HomePageActions;
import com.example.testng.actions.LoginPageActions;
import com.example.testng.utils.HelperClass;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
public class LoginPageDefinitions{
LoginPageActions objLogin = new LoginPageActions();
HomePageActions objHomePage = new HomePageActions();
@Given("User is on HRMLogin page {string}")
public void loginTest(String url) {
HelperClass.openPage(url);
}
@When("User enters username as {string} and password as {string}")
public void goToHomePage(String userName, String passWord) {
// login to application
objLogin.login(userName, passWord);
// go the next page
}
@Then("User should be able to login sucessfully and new page opens")
public void verifyLogin() {
// Verify home page
Assert.assertTrue(objHomePage.getHomePageText().contains("Employee Information"));
}
@Then("User should be able to see error message {string}")
public void verifyErrorMessage(String expectedErrorMessage) {
// Verify home page
Assert.assertEquals(objLogin.getErrorMessage(),expectedErrorMessage);
}
@Then("User should be able to see FaceBook Icon")
public void verifyFaceBookIcon( ) {
Assert.assertTrue(objLogin.getFaceBookIcon());
}
@Then("User should be able to see error message for empty username as {string}")
public void verifyErrorMessageForEmptyUsername(String expectedErrorMessage) {
Assert.assertEquals(objLogin.getMissingUsernameText(),expectedErrorMessage);
}
}
Step 8: Create Hook class in src/test/java
Create the hook class that contains the Before and After hooks. @Before hook contains the method to call the setup driver which will initialize the chrome driver. This will be run before any test.
After Hook – Here will call the tearDown method.
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import com.example.testng.utils.HelperClass;
import io.cucumber.java.After;
import io.cucumber.java.Before;
import io.cucumber.java.Scenario;
public class Hooks {
@Before
public static void setUp() {
HelperClass.setUpDriver();
}
@After
public static void tearDown(Scenario scenario) {
//validate if scenario has failed
if(scenario.isFailed()) {
final byte[] screenshot = ((TakesScreenshot) HelperClass.getDriver()).getScreenshotAs(OutputType.BYTES);
scenario.attach(screenshot, "image/png", scenario.getName());
}
HelperClass.tearDown();
}
}
Step 9: Create a Cucumber Test Runner class in src/test/java
Add the extent report cucumber adapter to the runner class’s CucumberOption annotation.
plugin = {"com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:"}
This is how your runner class should look after being added to our project. Moreover, be sure to keep the colon “:” at the end.
import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
@CucumberOptions(tags = "", features = "src/test/resources/features/LoginPage.feature", glue = "com.example.testng.definitions",
plugin = {"com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:"})
public class CucumberRunnerTests extends AbstractTestNGCucumberTests {
}
Step 10: Create the testng.xml for the project
Right-click on the project and select TestNG -> convert to TestNG.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite">
<test name="ExtentReport4 for Cucumber">
<classes>
<class name = "com.example.testng.runner.CucumberRunnerTests"/>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
Step 11: Execute the code
Right Click on the Runner class and select Run As -> TestNG Test.
Below is the screenshot of the Console. As expected, 5 tests, out of 7 are passed and 2 failed.

Step 12: View ExtentReport
Refresh the project and will see a new folder – Report. The ExtentReport will be present in that folder with the name Spark.html.
Right-click and select Open with Web Browser.
The report also 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 features as shown in the image below.


Click on the Dashboard icon present on the left side of the report. To view the details about the steps, click on the scenarios. Clicking on the scenario will expand, showing off the details of the steps of each scenario.

The icon present at the end of the failed scenario is highlighted, click on that icon. It is the screenshot of the failed test.

Logger Report
This is the Dashboard Report.

Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!! Cheers!!
Gradle – ExtentReports Version 5 for Cucumber, Selenium and JUnit4
The previous tutorial explained the generation of ExtentReports Version 5 for Cucumber 7 and TestNG in a Maven project. In this tutorial, I will explain the steps to create an Extent Report Version 5 for Cucumber, Selenium, and Junit4 in a Gradle project.
Pre Requisite:
- Java 8 or above installed
- Eclipse or IntelliJ IDE installed
- Gradle Installed
- Environment variable JAVA_HOME and GRADLE_HOME correctly configured
In this tutorial, I’ll create a BDD Framework for the testing of web applications using Cucumber, Selenium WebDriver with JUnit4. This framework consists of:-
- Cucumber Java- 7.6.0
- Cucumber JUnit – 7.6.0
- Java 11
- JUnit – 4.13.2
- Gradle – 7.5.1
- Selenium – 4.3.0
- ExtentReport – 5.0.9
- GrassHopper Cucumber Adapter – 1.7.0
Implementation Steps
- Add ExtentReport dependency to the build.gradle
- Add ExtentCucumberAdapter plugin to task cucumber
- Add Cucumber, Selenium and JUnit4 , and dependencies in build.gradle
- Create Locator and Action classes and Step Definition corresponding to the feature file
- Create extent.properties file in resources folder and paste the below code
- Execute the Tests
- View the ExtentReports
There is a tutorial that explains the Integration of Cucumber, Selenium, and JUnit4 in a Gradle project. Please refer to this tutorial – Gradle Project with Cucumber, Selenium, and JUnit4.
Step 1 – Add ExtentReport dependency to the build.gradle
To create ExtentReport, we need to add the below-mentioned dependency in build.gradle.
implementation 'tech.grasshopper:extentreports-cucumber7-adapter:1.7.0'
implementation 'com.aventstack:extentreports:5.0.9'
Step 2 – Add ExtentCucumberAdapter plugin to task cucumber
task cucumber() {
dependsOn assemble, compileTestJava
doLast {
javaexec {
main = "io.cucumber.core.cli.Main"
classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
args = ['--plugin', 'pretty',
'--plugin', 'io.qameta.allure.cucumber7jvm.AllureCucumber7Jvm',
'--plugin', 'com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:',
'--glue', 'com.example.definitions', 'src/test/resources']
}
}
}
Step 3 – Add Cucumber, Selenium and JUnit4, and dependencies in build.gradle
dependencies {
testImplementation 'io.cucumber:cucumber-java:7.6.0'
testImplementation 'io.cucumber:cucumber-junit:7.6.0'
// Use JUnit test framework.
testImplementation 'junit:junit:4.13.2'
//ExtentReport
implementation 'tech.grasshopper:extentreports-cucumber7-adapter:1.7.0'
implementation 'com.aventstack:extentreports:5.0.9'
// This dependency is used by the application.
implementation 'com.google.guava:guava:30.1.1-jre'
implementation 'org.seleniumhq.selenium:selenium-java:4.4.0'
implementation 'io.github.bonigarcia:webdrivermanager:5.3.0'
}
The complete build.gradle is shown below:
/*
* This file was generated by the Gradle 'init' task.
*
*/
plugins {
// Apply the application plugin to add support for building a CLI application in Java.
id 'application'
}
repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}
java {
sourceCompatibility = 11
targetCompatibility = 11
}
dependencies {
testImplementation 'io.cucumber:cucumber-java:7.6.0'
testImplementation 'io.cucumber:cucumber-junit:7.6.0'
// Use JUnit test framework.
testImplementation 'junit:junit:4.13.2'
//ExtentReport
implementation 'tech.grasshopper:extentreports-cucumber7-adapter:1.7.0'
implementation 'com.aventstack:extentreports:5.0.9'
// This dependency is used by the application.
implementation 'com.google.guava:guava:30.1.1-jre'
implementation 'org.seleniumhq.selenium:selenium-java:4.4.0'
implementation 'io.github.bonigarcia:webdrivermanager:5.3.0'
}
application {
// Define the main class for the application.
mainClass = 'com.example.App'
}
configurations {
cucumberRuntime {
extendsFrom testImplementation
}
}
task cucumber() {
dependsOn assemble, testClasses
doLast {
javaexec {
main = "io.cucumber.core.cli.Main"
classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
args = ['--plugin', 'pretty',
'--plugin', 'io.qameta.allure.cucumber7jvm.AllureCucumber7Jvm',
'--plugin', 'com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:',
'--glue', 'com.example.definitions', 'src/test/resources']
}
}
}
Step 4 – Create Locator and Action classes and Step Definition corresponding to the feature file
As mentioned above, there is another tutorial that explains the project structure as well as the feature file and corresponding Step Definitions, please refer to this tutorial – Gradle Project with Cucumber, Selenium and JUnit4.
Step 5 – Create extent.properties file in resources folder and paste the below code
#Extent Report
extent.reporter.spark.start=true
extent.reporter.spark.out=Reports/Spark.html
#PDF Report
extent.reporter.pdf.start=true
extent.reporter.pdf.out=PdfReport/ExtentPdf.pdf
#HTML Report
extent.reporter.html.start=true
extent.reporter.html.out=HtmlReport/ExtentHtml.html
#FolderName
basefolder.name=ExtentReports/SparkReport_
basefolder.datetimepattern=d_MMM_YY HH_mm_ss
#Screenshot
screenshot.dir=/Screenshots/
screenshot.rel.path=../Screenshots/
#Base64
extent.reporter.spark.base64imagesrc=true
#System Info
systeminfo.os=windows
systeminfo.version=10
Step 6 – Execute the Tests
Go to the app project and run the tests, using the below command
gradle cucumber
The output of the above program is

Step 7: View the ExtentReports
Refresh the project and will see a new folder – SparkReport_ which further contains 4 folders –HtmlReport, PdfReport, Reports, and Screenshots.

The ExtentReport will be present in the Report’s folder with the name Spark.html. PDF Report is present in the PdfReport folder and HTML Report is present in the HtmlReport folder. We can see that the Screenshots’ folder is empty because we have used base64imagesrc feature which resulted in no physical screenshots. The screenshots are embedded in the reports.
Right-click and open the ExtentHtml.html report with the Web Browser. The report also 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 features as shown in the image below.
ExtentHtml.html

The failed test has screenshot embedded in it. Double click on mase64image and it will open the screenshot in full screen.

Screenshot of failed Test Case

PDF Report
To know more about PDF Report generation, please refer to this tutorial – PDF ExtentReport for Cucumber and TestNG.

Spark Report
Right-click and open the Spark.html report with Web Browser.

Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!! Cheers!!
Gradle – Extent Report Version 5 for Cucumber, Selenium, and TestNG
The previous tutorial explained the generation of Extent Reports Version 5 for Cucumber 7 and TestNG in a Maven project. In this tutorial, I will explain the steps to create an Extent Report Version 5 for Cucumber, Selenium, and TestNG in a Gradle project.
Pre Requisite
- Java 8 or above installed
- Eclipse or IntelliJ IDE installed
- Gradle Installed
- Environment variables JAVA_HOME and GRADLE_HOME are correctly configured
In this tutorial, I’ll create a BDD Framework for the testing of web applications using Cucumber, and Selenium WebDriver with TestNG. This framework consists of:-
- Cucumber Java- 7.6.0
- Cucumber JUnit– 7.6.0
- Java 11
- TestNG – 7.6.0
- Gradle – 7.5.1
- Selenium – 4.3.0
- ExtentReport – 5.0.9
- GrassHopper Cucumber Adapter – 1.7.0
Implementation Steps
- Add ExtentReport dependency to the build.gradle
- Add ExtentCucumberAdapter plugin to task cucumber
- Add Cucumber, Selenium and TestNG, and dependencies in build.gradle
- Create Locator and Action classes and Step Definition corresponding to the feature file
- Create extent.properties file in the resources folder and paste the below code
- Execute the Tests
- View the Extent Report
There is a tutorial that explains the Integration of Cucumber, Selenium, and TestNG in a Gradle project. Please refer to this tutorial – Gradle Project with Cucumber, Selenium and TestNG.
Step 1 – Add Extent Report dependency to the build.gradle
To create an Extent Report, we need to add the below-mentioned dependency in the build.gradle
implementation 'tech.grasshopper:extentreports-cucumber7-adapter:1.7.0'
implementation 'com.aventstack:extentreports:5.0.9'
Step 2 – Add ExtentCucumberAdapter plugin to task cucumber
task cucumber() {
dependsOn assemble, compileTestJava
doLast {
javaexec {
main = "io.cucumber.core.cli.Main"
classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
args = ['--plugin', 'pretty',
'--plugin', 'io.qameta.allure.cucumber7jvm.AllureCucumber7Jvm',
'--plugin', 'com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:',
'--glue', 'com.example.definitions', 'src/test/resources']
}
}
}
Step 3 – Add Cucumber, Selenium, and TestNG dependencies in build.gradle
dependencies {
testImplementation 'io.cucumber:cucumber-java:7.6.0'
testImplementation 'io.cucumber:cucumber-junit:7.6.0'
//TestNG
testImplementation 'org.testng:testng:7.6.0'
//ExtentReport
implementation 'tech.grasshopper:extentreports-cucumber7-adapter:1.7.0'
implementation 'com.aventstack:extentreports:5.0.9'
// This dependency is used by the application.
implementation 'com.google.guava:guava:30.1.1-jre'
implementation 'org.seleniumhq.selenium:selenium-java:4.4.0'
implementation 'io.github.bonigarcia:webdrivermanager:5.3.0'
}
The complete build.gradle is shown below:
plugins {
// Apply the application plugin to add support for building a CLI application in Java.
id 'application'
}
repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}
java {
sourceCompatibility = 11
targetCompatibility = 11
}
dependencies {
// Use TestNG framework, also requires calling test.useTestNG() below
testImplementation 'io.cucumber:cucumber-java:7.6.0'
testImplementation 'io.cucumber:cucumber-testng:7.6.0'
//TestNG
testImplementation 'org.testng:testng:7.6.0'
//ExtentReport
implementation 'tech.grasshopper:extentreports-cucumber7-adapter:1.7.0'
implementation 'com.aventstack:extentreports:5.0.9'
//Others
implementation 'com.google.guava:guava:31.0.1-jre'
implementation 'org.seleniumhq.selenium:selenium-java:4.4.0'
implementation 'io.github.bonigarcia:webdrivermanager:5.3.0'
}
application {
// Define the main class for the application.
mainClass = 'com.example.App'
}
tasks.named('test') {
// Use TestNG for unit tests.
useTestNG()
}
configurations {
cucumberRuntime {
extendsFrom testImplementation
}
}
task cucumber() {
dependsOn assemble, compileTestJava
doLast {
javaexec {
main = "io.cucumber.core.cli.Main"
classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
args = ['--plugin', 'pretty',
'--plugin', 'io.qameta.allure.cucumber7jvm.AllureCucumber7Jvm',
'--plugin', 'com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:',
'--glue', 'com.example.definitions', 'src/test/resources']
}
}
}
Step 4 – Create Locator and Action classes and Step Definition corresponding to the feature file
As mentioned above, there is another tutorial that explains the project structure as well as the feature file and corresponding Step Definitions, please refer to this tutorial – Gradle Project with Cucumber, Selenium, and TestNG.
Step 5 – Create extent.properties file in the resources folder and paste the below code
#Extent Report
extent.reporter.spark.start=true
extent.reporter.spark.out=Reports/Spark.html
#PDF Report
extent.reporter.pdf.start=true
extent.reporter.pdf.out=PdfReport/ExtentPdf.pdf
#HTML Report
extent.reporter.html.start=true
extent.reporter.html.out=HtmlReport/ExtentHtml.html
#FolderName
basefolder.name=ExtentReports/SparkReport_
basefolder.datetimepattern=d_MMM_YY HH_mm_ss
#Screenshot
screenshot.dir=/Screenshots/
screenshot.rel.path=../Screenshots/
#Base64
extent.reporter.spark.base64imagesrc=true
#System Info
systeminfo.os=windows
systeminfo.version=10
Step 6 – Execute the Tests
Go to the app project and run the tests, using the below command
gradle cucumber
The output of the above program is

Step 7: View the ExtentReports
Refresh the project and will see a new folder – SparkReport_ which further contains 4 folders -Html Report, Pdf Report, Reports, and Screenshots.

The Extent Report will be present in the Report’s folder with the name Spark.html. PDF Report is present in the Pdf Report folder and HTML Report is present in the HTML report folder. We can see that the Screenshot’s folder is empty because we have used the base64imagesrc feature, which resulted in no physical screenshots. The screenshots are embedded in the reports.
Right-click and open the ExtentHtml.html report with the Web Browser. The report also 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 features as shown in the image below.
ExtentHtml.html

The failed test has a screenshot embedded in it. Double-click on mase64image, and it will open the screenshot in full screen.

Screenshot of failed Test Case

PDF Report
To know more about PDF Report generation, please refer to this tutorial – PDF ExtentReport for Cucumber and TestNG.

Spark Report
Right-click and open the Spark.html report with the Web Browser.

Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!! Cheers!!
Extent Reports Version 5 for Cucumber7 and JUnit5
The previous tutorial explained the steps to generate ExtentReports Version for Cucumber7 with TestNG. This tutorial explains the steps needed to be followed to generate an ExtentReports Version5 for Cucumber 7.
Pre-Requisite:
- Java 8 or higher is needed for ExtentReport5
- Maven or Gradle
- JAVA IDE (like Eclipse, IntelliJ, or soon)
- Cucumber Eclipse plugin (in case using Eclipse)
Project Structure

There is a tutorial that explains the steps to integrate Cucumber 7 with JUnit5. Please refer to this tutorial – Integration of Cucumber7 with Selenium and JUnit5.
Now, let us add the extra steps needed to generate the ExtentRport Version5.
New Features in ExtentReports Version 5
Report Attachments
To add attachments, like screen images, two settings need to be added to the extent.properties. Firstly property, named screenshot.dir, is the directory where the attachments are stored. Secondly is screenshot.rel.path, which is the relative path from the report file to the screenshot directory.
extent.reporter.spark.out=Reports/Spark.html
screenshot.dir=/Screenshots/
screenshot.rel.path=../Screenshots/
Extent PDF Reporter
The PDF reporter summarizes the test run results in a dashboard and other sections with the feature, scenario, and, step details. The PDF report needs to be enabled in the extent.properties file.
#PDF Report
extent.reporter.pdf.start=true
extent.reporter.pdf.out=PdfReport/ExtentPdf.pdf
Ported HTML Reporter
The original HTML Extent Reporter was deprecated in 4.1.3 and removed in 5.0.0. The HTML report available in the adapter is based on the same code base and is similar in appearance. The major changes are in the Freemarker template code which has been modified to work with the Extent Reports version 5. The HTML report needs to be enabled in the extent.properties file.
#HTML Report
extent.reporter.html.start=true
extent.reporter.html.out=HtmlReport/ExtentHtml.html
Customized Report Folder Name
To enable the report folder name with date and\or time details, two settings need to be added to the extent.properties. These are basefolder.name and basefolder.datetimepattern. These will be merged to create the base folder name, inside which the reports will be generated.
#FolderName
basefolder.name=ExtentReports/SparkReport_
basefolder.datetimepattern=d_MMM_YY HH_mm_ss
Attach Image as Base64 String
This feature can be used to attach images to the Spark report by setting the src attribute of the img tag to a Base64 encoded string of the image. When this feature is used, no physical file is created. There is no need to modify any step definition code to use this. To enable this, use the below settings in extent.properties, which is false by default.
extent.reporter.spark.base64imagesrc=true
Environment or System Info Properties
It is now possible to add environment or system info properties in the extent.properties or pass them in the maven command line.
#System Info
systeminfo.os=windows
systeminfo.version=10
As mentioned above, refer to this tutorial.
Step 1 – Add Maven dependencies to the POM
Add ExtentReport dependency
<dependency>
<groupId>com.aventstack</groupId>
<artifactId>extentreports</artifactId>
<version>5.0.9</version>
</dependency>
Add tech grasshopper maven dependency for Cucumber.
<dependency>
<groupId>tech.grasshopper</groupId>
<artifactId>extentreports-cucumber7-adapter</artifactId>
<version>1.7.0</version>
</dependency>
The complete POM.xml will look like as shown below with other Selenium and JUnit5 dependencies.
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>ExtentReportsCucumber7JUnit5</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<cucumber.version>7.6.0</cucumber.version>
<extentreports.cucumber7.adapter.version>1.7.0</extentreports.cucumber7.adapter.version>
<extentreports.version>5.0.9</extentreports.version>
<selenium.version>4.3.0</selenium.version>
<webdrivermanager.version>5.2.1</webdrivermanager.version>
<junit.jupiter.version>5.9.0</junit.jupiter.version>
<apache.common.version>2.4</apache.common.version>
<projectlombok.version>1.18.24</projectlombok.version>
<maven.compiler.plugin.version>3.10.1</maven.compiler.plugin.version>
<maven.surefire.plugin.version>3.0.0-M7</maven.surefire.plugin.version>
<maven.compiler.source.version>11</maven.compiler.source.version>
<maven.compiler.target.version>11</maven.compiler.target.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-bom</artifactId>
<version>${cucumber.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>${junit.jupiter.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit-platform-engine</artifactId>
<scope>test</scope>
</dependency>
<!-- JUnit Platform -->
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<!-- Cucumber ExtentReport Adapter -->
<dependency>
<groupId>tech.grasshopper</groupId>
<artifactId>extentreports-cucumber7-adapter</artifactId>
<version>${extentreports.cucumber7.adapter.version}</version>
</dependency>
<!-- Extent Report -->
<dependency>
<groupId>com.aventstack</groupId>
<artifactId>extentreports</artifactId>
<version>${extentreports.version}</version>
</dependency>
<!-- Selenium -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
</dependency>
<!-- Web Driver Manager -->
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>${webdrivermanager.version}</version>
</dependency>
<!-- Apache Common -->
<dependency>
<groupId>org.apache.directory.studio</groupId>
<artifactId>org.apache.commons.io</artifactId>
<version>${apache.common.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${projectlombok.version}</version>
<scope>provided</scope>
</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>
<properties>
<configurationParameters>
cucumber.junit-platform.naming-strategy=long
</configurationParameters>
</properties>
</configuration>
</plugin>
</plugins>
</build>
</project>
Step 2 – Create extent.properties file in src/test/resources
We need to create the extent.properties file in the src/test/resources folder for the grasshopper extent report adapter to recognize it. Using a property file for reporting is quite helpful if you want to define several different properties.
#Extent Report
extent.reporter.spark.start=true
extent.reporter.spark.out=Reports/Spark.html
#PDF Report
extent.reporter.pdf.start=true
extent.reporter.pdf.out=PdfReport/ExtentPdf.pdf
#HTML Report
extent.reporter.html.start=true
extent.reporter.html.out=HtmlReport/ExtentHtml.html
#FolderName
basefolder.name=ExtentReports/SparkReport_
basefolder.datetimepattern=d_MMM_YY HH_mm_ss
#Screenshot
screenshot.dir=/Screenshots/
screenshot.rel.path=../Screenshots/
#Base64
extent.reporter.spark.base64imagesrc=true
#System Info
systeminfo.os=windows
systeminfo.version=10
Step 3 – Create a Cucumber Test Runner class in src/test/java
Add the extent report cucumber adapter to the runner class.
import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME;
import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME;
import org.junit.platform.suite.api.ConfigurationParameter;
import org.junit.platform.suite.api.IncludeEngines;
import org.junit.platform.suite.api.SelectClasspathResource;
import org.junit.platform.suite.api.Suite;
@Suite
@IncludeEngines("cucumber")
@SelectClasspathResource("com.example")
@ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:")
@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "com.example")
public class CucumberRunnerTests {
}
Step 4 – Execute the code
To execute the code, run the tests from the command line by using the below command
mvn clean test -Dcucumber.features="src/test/resources/features"

Step 5 – View ExtentReport
Refresh the project and will see a new folder – SparkReport_ which further contains 4 folders – HtmlReport, PdfReport, Reports, and Screenshots.

The ExtentReport will be present in the Reports folder with the name Spark.html. PDF Report is present in the PdfReport folder and HTML Report is present in the HtmlReport folder. We can see that the Screenshots folder is empty because we have used the base64imagesrc feature which resulted in no physical screenshots. The screenshots are embedded in the reports.
Right-click and open the ExtentHtml.html report with Web Browser. The report also 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 features as shown in the image below.
ExtentHtml
This is the image of the Dashboard of the ExtentReport

The failed test has a screenshot embedded in it. Double-click on mase64image and it will open the screenshot in full screen.


PDF Report

Spark Report
Right-click and open the Spark.html report with Web Browser.

We are done! Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!!
ExtentReports Version 5 for Cucumber 7 and TestNG
The previous tutorial explained the steps to generate ExtentReports Version for Cucumber6 with TestNG. This tutorial explains the steps needed to be followed to generate an ExtentReports Version5 for Cucumber 7.
New Features in ExtentReports Version 5
Report Attachments
To add attachments, like screen images, two settings need to be added to the extent.properties. Firstly property, named screenshot.dir, is the directory where the attachments are stored. Secondly is screenshot.rel.path, which is the relative path from the report file to the screenshot directory.
extent.reporter.spark.out=Reports/Spark.html
screenshot.dir=/Screenshots/
screenshot.rel.path=../Screenshots/
Extent PDF Reporter
The PDF reporter summarizes the test run results in a dashboard and other sections with feature, scenario, and, step details. The PDF report needs to be enabled in the extent.properties file.
#PDF Report
extent.reporter.pdf.start=true
extent.reporter.pdf.out=PdfReport/ExtentPdf.pdf
Ported HTML Reporter
The original HTML Extent Reporter was deprecated in 4.1.3 and removed in 5.0.0. The HTML report available in the adapter is based on the same code base and is similar in appearance. The major changes are in the Freemarker template code which has been modified to work with the Extent Reports version 5. The HTML report needs to be enabled in the extent.properties file.
#HTML Report
extent.reporter.html.start=true
extent.reporter.html.out=HtmlReport/ExtentHtml.html
Customized Report Folder Name
To enable the report folder name with date and\or time details, two settings need to be added to the extent.properties. These are basefolder.name and basefolder.datetimepattern. These will be merged to create the base folder name, inside which the reports will be generated.
#FolderName
basefolder.name=ExtentReports/SparkReport_
basefolder.datetimepattern=d_MMM_YY HH_mm_ss
Attach Image as Base64 String
This feature can be used to attach images to the Spark report by setting the src attribute of the img tag to a Base64 encoded string of the image. When this feature is used, no physical file is created. There is no need to modify any step definition code to use this. To enable this, use the below settings in extent.properties, which is false by default.
extent.reporter.spark.base64imagesrc=true
Environment or System Info Properties
It is now possible to add environment or system info properties in the extent.properties or pass them in the maven command line.
#System Info
systeminfo.os=windows
systeminfo.version=10
Generation of ExtentReport 5 in Cucumber7 with TestNG
Pre-Requisite:
- Java 8 or higher is needed for ExtentReport5
- Maven or Gradle
- JAVA IDE (like Eclipse, IntelliJ, or soon)
- TestNG installed
- Cucumber Eclipse plugin (in case using Eclipse)
Project Structure

Step 1 – Add Maven dependencies to the POM
Add ExtentReport dependency
<dependency>
<groupId>com.aventstack</groupId>
<artifactId>extentreports</artifactId>
<version>5.0.9</version>
</dependency>
Add tech grasshopper maven dependency for Cucumber
<dependency>
<groupId>tech.grasshopper</groupId>
<artifactId>extentreports-cucumber7-adapter</artifactId>
<version>1.7.0</version>
</dependency>
The complete POM.xml will look like as shown below with other Selenium and TestNG dependencies.
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<cucumber.version>7.6.0</cucumber.version>
<extentreports.cucumber7.adapter.version>1.7.0</extentreports.cucumber7.adapter.version>
<extentreports.version>5.0.9</extentreports.version>
<selenium.version>4.3.0</selenium.version>
<webdrivermanager.version>5.2.1</webdrivermanager.version>
<testng.version>7.6.1</testng.version>
<apache.common.version>2.4</apache.common.version>
<maven.compiler.plugin.version>3.10.1</maven.compiler.plugin.version>
<maven.surefire.plugin.version>3.0.0-M7</maven.surefire.plugin.version>
<maven.compiler.source.version>11</maven.compiler.source.version>
<maven.compiler.target.version>11</maven.compiler.target.version>
</properties>
<dependencies>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>${cucumber.version}</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-testng</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>
<!-- Cucumber ExtentReport Adapter -->
<dependency>
<groupId>tech.grasshopper</groupId>
<artifactId>extentreports-cucumber7-adapter</artifactId>
<version>${extentreports.cucumber7.adapter.version}</version>
</dependency>
<!-- Extent Report -->
<dependency>
<groupId>com.aventstack</groupId>
<artifactId>extentreports</artifactId>
<version>${extentreports.version}</version>
</dependency>
<!-- Selenium -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
</dependency>
<!-- Web Driver Manager -->
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>${webdrivermanager.version}</version>
</dependency>
<!-- TestNG -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
<!-- Apache Common -->
<dependency>
<groupId>org.apache.directory.studio</groupId>
<artifactId>org.apache.commons.io</artifactId>
<version>${apache.common.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>
Step 2: Create a feature file in src/test/resources
Below is a sample feature file. I have also added a failed scenario in @FaceBookLink.
Feature: Login to HRM Application
@ValidCredentials
Scenario: Login with valid credentials
Given User is on HRMLogin page "https://opensource-demo.orangehrmlive.com/"
When User enters username as "Admin" and password as "admin123"
Then User should be able to login sucessfully and new page open
@InvalidCredentials
Scenario Outline: Login with invalid credentials
Given User is on HRMLogin page "https://opensource-demo.orangehrmlive.com/"
When User enters username as "<username>" and password as "<password>"
Then User should be able to see error message "<errorMessage>"
Examples:
| username | password | errorMessage |
| | abc | Username cannot be empty |
| admin | | Password cannot be empty |
| | | Username cannot be empty |
| Admin | admin12$$ | Invalid credentials |
| admin$$ | admin123 | Invalid credentials |
@FaceBookLink
Scenario: Verify FaceBook Icon on Login Page
Given User is on HRMLogin page "https://opensource-demo.orangehrmlive.com/"
Then User should be able to see FaceBook Icon
@LinkedInLink
Scenario: Verify LinkedIn Icon on Login Page
Given User is on HRMLogin page "https://opensource-demo.orangehrmlive.com/"
Then User should be able to see LinkedIn Icon
Step 3: Create extent.properties file in src/test/resources
We need to create the extent.properties file in the src/test/resources folder for the grasshopper extent report adapter to recognize it. Using a property file for reporting is quite helpful if you want to define several different properties.
extent.reporter.spark.start=true
extent.reporter.spark.out=Reports/Spark.html
#PDF Report
extent.reporter.pdf.start=true
extent.reporter.pdf.out=PdfReport/ExtentPdf.pdf
#HTML Report
extent.reporter.html.start=true
extent.reporter.html.out=HtmlReport/ExtentHtml.html
#FolderName
basefolder.name=ExtentReports/SparkReport_
basefolder.datetimepattern=d_MMM_YY HH_mm_ss
#Screenshot
screenshot.dir=/Screenshots/
screenshot.rel.path=../Screenshots/
#Base64
extent.reporter.spark.base64imagesrc=true
#System Info
systeminfo.os=windows
systeminfo.version=10
Step 4: Create a Helper class in src/main/java
We have used Page Object Model with Cucumber and TestNG. Create a Helper class where we are initializing the web driver, initializing the web driver wait, defining the timeouts, and creating a private constructor of the class, it will declare the web driver, so whenever we create an object of this class, a new web browser is invoked.
import java.time.Duration;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
import io.github.bonigarcia.wdm.WebDriverManager;
public class HelperClass {
private static HelperClass helperClass;
private static WebDriver driver;
private static WebDriverWait wait;
public final static int TIMEOUT = 10;
private HelperClass() {
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
wait = new WebDriverWait(driver, Duration.ofSeconds(TIMEOUT));
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(TIMEOUT));
driver.manage().window().maximize();
}
public static void openPage(String url) {
driver.get(url);
}
public static WebDriver getDriver() {
return driver;
}
public static void setUpDriver() {
if (helperClass==null) {
helperClass = new HelperClass();
}
}
public static void tearDown() {
if(driver!=null) {
driver.close();
driver.quit();
}
helperClass = null;
}
}
Step 5: Create Locator classes in src/main/java
Create a locator class for each page that contains the detail of the locators of all the web elements. Here, I’m creating 2 locator classes – LoginPageLocators and HomePageLocators.
LoginPageLocators
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class LoginPageLocators {
@FindBy(name = "username")
public WebElement userName;
@FindBy(name = "password")
public WebElement password;
@FindBy(xpath = "//*[@id='app']/div[1]/div/div[1]/div/div[2]/div[2]/form/div[3]/button")
public WebElement login;
@FindBy(xpath = "//*[@id='app']/div[1]/div/div[1]/div/div[2]/div[2]/div/div[1]/div[1]/p")
public WebElement errorMessage;
@FindBy(xpath = "//*[@href='https://www.linkedin.com/company/orangehrm/mycompany/']")
public WebElement linkedInIcon;
@FindBy(xpath = "//*[@href='https://www.facebook.com/OrangeHRM/mycompany']") //Invalid Xpath
public WebElement faceBookIcon;
}
HomePageLocators
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class HomePageLocators {
@FindBy(xpath = "//*[@id='app']/div[1]/div[2]/div[2]/div/div[1]/div[1]/div[1]/h5")
public WebElement homePageUserName;
}
Step 6: Create Action classes in src/main/java
Create the action classes for each web page. These action classes contain all the methods needed by the step definitions. In this case, I have created 2 action classes – LoginPageActions and HomePageActions.
LoginPageActions
In this class, the very first thing will do is to create the object of LoginPageLocators class so that we should be able to access all the PageFactory elements. Secondly, create a public constructor of LoginPageActions class
import org.openqa.selenium.support.PageFactory;
import com.example.locators.LoginPageLocators;
import com.example.utils.HelperClass;
public class LoginPageActions {
LoginPageLocators loginPageLocators = null;
public LoginPageActions() {
this.loginPageLocators = new LoginPageLocators();
PageFactory.initElements(HelperClass.getDriver(),loginPageLocators);
}
// Set user name in textbox
public void setUserName(String strUserName) {
loginPageLocators.userName.sendKeys(strUserName);
}
// Set password in password textbox
public void setPassword(String strPassword) {
loginPageLocators.password.sendKeys(strPassword);
}
// Click on login button
public void clickLogin() {
loginPageLocators.login.click();
}
// Get the error message of Login Page
public String getErrorMessage() {
return loginPageLocators.errorMessage.getText();
}
// LinkedIn Icon is displayed
public Boolean getLinkedInIcon() {
return loginPageLocators.linkedInIcon.isDisplayed();
}
// FaceBook Icon is displayed
public Boolean getFaceBookIcon() {
return loginPageLocators.faceBookIcon.isDisplayed();
}
public void login(String strUserName, String strPassword) {
// Fill user name
this.setUserName(strUserName);
// Fill password
this.setPassword(strPassword);
// Click Login button
this.clickLogin();
}
}
HomePageActions
import org.openqa.selenium.support.PageFactory;
import com.example.locators.HomePageLocators;
import com.example.utils.HelperClass;
public class HomePageActions {
HomePageLocators homePageLocators = null;
public HomePageActions() {
this.homePageLocators = new HomePageLocators();
PageFactory.initElements(HelperClass.getDriver(),homePageLocators);
}
// Get the User name from Home Page
public String getHomePageText() {
return homePageLocators.homePageUserName.getText();
}
}
Step 7: Create a Step Definition file in src/test/java
Create the corresponding Step Definition file of the feature file.
LoginPageDefinitions
import org.testng.Assert;
import com.example.actions.HomePageActions;
import com.example.actions.LoginPageActions;
import com.example.utils.HelperClass;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
public class LoginPageDefinitions{
LoginPageActions objLogin = new LoginPageActions();
HomePageActions objHomePage = new HomePageActions();
@Given("User is on HRMLogin page {string}")
public void loginTest(String url) {
HelperClass.openPage(url);
}
@When("User enters username as {string} and password as {string}")
public void goToHomePage(String userName, String passWord) {
// login to application
objLogin.login(userName, passWord);
// go the next page
}
@Then("User should be able to login sucessfully and new page open")
public void verifyLogin() {
// Verify home page
Assert.assertTrue(objHomePage.getHomePageText().contains("Employee Information"));
}
@Then("User should be able to see error message {string}")
public void verifyErrorMessage(String expectedErrorMessage) {
// Verify home page
Assert.assertEquals(objLogin.getErrorMessage(),expectedErrorMessage);
}
@Then("User should be able to see LinkedIn Icon")
public void verifyLinkedInIcon( ) {
Assert.assertTrue(objLogin.getLinkedInIcon());
}
@Then("User should be able to see FaceBook Icon")
public void verifyFaceBookIcon( ) {
Assert.assertTrue(objLogin.getFaceBookIcon());
}
}
Step 8: Create Hook class in src/test/java
Create the hook class that contains the Before and After hook. @Before hook contains the method to call the setup driver which will initialize the chrome driver. This will be run before any test.
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import com.example.utils.HelperClass;
import io.cucumber.java.After;
import io.cucumber.java.Before;
import io.cucumber.java.Scenario;
public class Hooks {
@Before
public static void setUp() {
HelperClass.setUpDriver();
}
@After
public static void tearDown(Scenario scenario) {
//validate if scenario has failed
if(scenario.isFailed()) {
final byte[] screenshot = ((TakesScreenshot) HelperClass.getDriver()).getScreenshotAs(OutputType.BYTES);
scenario.attach(screenshot, "image/png", scenario.getName());
}
HelperClass.tearDown();
}
}
Step 9: Create a Cucumber Test Runner class in src/test/java
Add the extent report cucumber adapter to the runner class’s CucumberOption annotation.
plugin = {"com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:"})
The updated Cucumber Runner class looks like as shown below:
import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
@CucumberOptions(tags = "", features = "src/test/resources/features/LoginPage.feature", glue = "com.example.definitions",
plugin = {"com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:"})
public class CucumberRunnerTests extends AbstractTestNGCucumberTests {
}
Step 10: Create the testng.xml for the project
Right-click on the project and select TestNG -> convert to TestNG.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite">
<test name="ExtentReport5 for Cucumber7">
<classes>
<class name = "com.example.runner.CucumberRunnerTests"/>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
Step 11: Execute the code
Right Click on the Runner class and select Run As -> TestNG Test.
Below is the screenshot of the Console. As expected, 4 tests, out of 5 are passed and 1 failed.

Step 12: View ExtentReport
Refresh the project and will see a new folder – SparkReport_ which further contains 4 folders – HtmlReport, PdfReport, Reports, and Screenshots.

The ExtentReport will be present in the Reports folder with the name Spark.html. PDF Report is present in PdfReport folder and HTML Report is present in HtmlReport folder. We can see that the Screenshots folder is empty because we have used base64imagesrc feature that results in no physical screenshots. The screenshots are embedded in the reports.
Right-click and open the ExtentHtml.html report with Web Browser. The report also 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 features as shown in the image below.
ExtentHtml.html

The failed test has a screenshot embedded in it. Double-click on mase64image and it will open the screenshot in full screen.

Screenshot of failed Test Case

PDF Report

Spark Report
Right-click and open the Spark.html report with Web Browser.

Congratulations on making it through this tutorial and hope you found it useful! Happy Learning!! Cheers!!
PDF ExtentReport for Cucumber and TestNG
In this tutorial, we will generate PDF reports using an Extent Adapter.
Step 1: Follow this article to add POM.xml, create and configure the sample project with the extent property file, and add the plugin to the Test Runner class.
Step 2: To attach the screenshot to your extent report, refer to How to add Screenshot to Cucumber ExtentReports.
Step 3: Add additional PDF-related properties in extent.properties.
extent.reporter.spark.start=true
extent.reporter.spark.out=Reports/Spark.html
#PDF Report
extent.reporter.pdf.start=true
extent.reporter.pdf.out=PdfReport/ExtentPdf.pdf
#FolderName
basefolder.name=ExtentReports/SparkReport_
basefolder.datetimepattern=d_MMM_YY HH_mm_ss
#Screenshot
screenshot.dir=/Screenshots/
screenshot.rel.path=../Screenshots/
Execute the test code. The PDF report will be generated as shown below:

The report contains six sections – dashboard, summary, tags, features, scenarios, and detailed sections.
1. Dashboard
This section is a single-page dashboard that summarizes the test run. This contains the report title, duration, and status of breakups.

2. Summary section
This section provides an overview of the test run in terms of a feature breakdown, comprising duration, scenario count, and step count. The scenarios and steps are divided into status counts. The feature name has a link that navigates to further details in the detailed step section. This link is only present if the detailed section is enabled.

3. Tag section
This section provides an overview of the test run in terms of a tag breakdown, comprising feature count and scenario count.

4. Feature section
This section describes the feature details with a stacked bar chart and a table of the scenario status and duration. This section display can be controlled by a configuration setting, enabled by default. The feature name has a link that navigates to further details in the detailed step section. This link is only present if the detailed section is enabled.

5. Scenario section
This section describes the scenario details with a stacked bar chart and a table of the step status and duration. This section display can be controlled by a configuration setting, enabled by default. The feature and scenario names have a link that navigates to further details in the detailed step section. This link is only present if the detailed section is enabled.

6. Detailed section
This section describes the details of individual steps and hooks, along with status and duration. This section display can be controlled by a configuration setting, enabled by default.



This section also contains screenshots of the failed images.
Customized PDF Report
The report settings can be used to toggle on and off optional report sections, and change the report title, text color for various data, background color, and other options.
The settings are saved in a YAML file called pdf-config.yaml, which is located in the project’s src/test/resources folder. If the file is missing or no settings are specified, the default values are used. To change the default values, create a pdf-config.yaml file in the project’s src/test/resources folder that contains only the new values for the settings.

A sample YAML configuration file is shown below:
passColor: 05a167
failColor: ff00ff
skipColor: a89132
displayFeature: true
displayScenario: true
displayDetailed: true
displayAttached: false
displayExpanded: true
dashboardConfig:
title: Cucumber PDF Report
dataBackgroundColor: 4F0CC8
titleColor: FF0000
dateColor: 969696
timeColor: 000000
dial:
featureRanges: 60 95
scenarioRanges: 70 90
stepRanges: 75 85
badColor: f768a1
averageColor: 93ffff
goodColor: 32ecab
summaryConfig:
totalColor: FF0000
durationColor: FF0000
tagConfig:
totalColor: FF0000
featureConfig:
totalColor: FF0000
durationColor: FF0000
scenarioConfig:
totalColor: FF0000
durationColor: FF0000
detailedFeatureConfig:
featureNameColor: FF0000
startEndTimeColor: 000000
tagColor: 404040
dataHeaderColor: FFFFFF
dataBackgroundColor: 404040
totalColor: 0000FF
durationColor: FFFFFF
durationBackgroundColor: 404040
detailedScenarioConfig:
featureNameColor: 404040
scenarioNameColor: FF0000
startEndTimeColor: 000000
tagColor: 404040
dataHeaderColor: FFFFFF
dataBackgroundColor: 404040
totalColor: 0000FF
durationColor: FFFFFF
durationBackgroundColor: 404040
stepChartBarColor: 7f32a8
detailedStepHookConfig:
stepTextColor: 0000FF
stepBackgroundColor: FFFFFF
hookTextColor: 00FF00
hookBackgroundColor: FFFFFF
durationColor: FF0000
errorMsgColor: 000000
logMsgColor: 000000
Execute the test code. Now the PDF Report will be generated as shown below:

This method of configuring report settings using a yaml properties file can be used both for the Maven plugin report generation and the ExtentReport style.
The passed, failed, and skipped colors can be set with the passColor, failColor and skipColor properties. These take in the colors in hex values (without the leading ‘#’) and are valid throughout the report.
The features, scenarios, and detailed sections can be displayed by setting the displayFeature, displayScenario and displayDetailed properties to true. The default value for these settings is true.

Screenshots are displayed as thumbnails and can be opened in the available native application. This is the default behaviour, in which the screenshot file is embedded in the PDF file. This can be toggled by the displayAttached setting. When the setting is set to false, only the thumbnail is displayed. These can also be displayed in zoomed images in a separate section by setting the displayExpanded to true and also displayAttached to false.
To know more about various settings in PDF Report, refer to this tutorial.
ExtentReports Version 5 for Cucumber 6 and JUnit4
The previous tutorial explained the steps to generate ExtentReports for Cucumber with TestNG. We can generate ExtentReports for Cucumber with JUnit4 also. This tutorial explains the steps that need to be followed to generate an ExtentReports Version5.
Pre-Requisite:
- Java 8 or higher is needed for ExtentReport5
- Maven or Gradle
- JAVA IDE (like Eclipse, IntelliJ, or soon)
- JUnit4 installed
- Cucumber Eclipse plugin (in case using Eclipse)
Project Structure

Step 1 – Add Maven dependencies to the POM
Add ExtentReport dependency
<dependency>
<groupId>com.aventstack</groupId>
<artifactId>extentreports</artifactId>
<version>5.0.9</version>
</dependency>
Add tech grasshopper maven dependency for Cucumber
<dependency>
<groupId>tech.grasshopper</groupId>
<artifactId>extentreports-cucumber6-adapter</artifactId>
<version>2.13.0</version>
</dependency>
The complete POM.xml will look like as shown below with other Selenium and JUnit4 dependencies.
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<selenium.version>4.3.0</selenium.version>
<cucumber.version>6.11.0</cucumber.version>
<extentreports.cucumber6.adapter.version>2.13.0</extentreports.cucumber6.adapter.version>
<extentreports.version>5.0.9</extentreports.version>
<junit.version>4.13.2</junit.version>
<apache.common.version>2.4</apache.common.version>
<webdrivermanager.version>5.2.1</webdrivermanager.version>
<maven.compiler.plugin.version>3.10.1</maven.compiler.plugin.version>
<maven.surefire.plugin.version>3.0.0-M7</maven.surefire.plugin.version>
<maven.compiler.source.version>11</maven.compiler.source.version>
<maven.compiler.target.version>11</maven.compiler.target.version>
</properties>
<dependencies>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>${cucumber.version}</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/tech.grasshopper/extentreports-cucumber6-adapter -->
<dependency>
<groupId>tech.grasshopper</groupId>
<artifactId>extentreports-cucumber6-adapter</artifactId>
<version>${extentreports.cucumber6.adapter.version}</version>
</dependency>
<!-- Extent Report -->
<dependency>
<groupId>com.aventstack</groupId>
<artifactId>extentreports</artifactId>
<version>${extentreports.version}</version>
</dependency>
<!-- Junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- Apache Common -->
<dependency>
<groupId>org.apache.directory.studio</groupId>
<artifactId>org.apache.commons.io</artifactId>
<version>${apache.common.version}</version>
</dependency>
<!-- Selenium -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
</dependency>
<!-- Web Driver Manager -->
<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-compiler-plugin</artifactId>
<version>${maven.compiler.plugin.version}</version>
<configuration>
<source>${maven.compiler.source.version}</source> <!--For JAVA 8 use 1.8-->
<target>${maven.compiler.target.version}</target> <!--For JAVA 8 use 1.8-->
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
Step 2: Create a feature file in src/test/resources
Below is a sample feature file.
Feature: Login to HRM Application
@ValidCredentials
Scenario: Login with valid credentials
Given User is on HRMLogin page "https://opensource-demo.orangehrmlive.com/"
When User enters username as "Admin" and password as "admin123"
Then User should be able to login successfully and new page open
@InvalidCredentials
Scenario Outline: Login with invalid credentials
Given User is on HRMLogin page "https://opensource-demo.orangehrmlive.com/"
When User enters username as "<username>" and password as "<password>"
Then User should be able to see error message "<errorMessage>"
Examples:
| username | password | errorMessage |
| | abc | Username cannot be empty |
| admin | | Password cannot be empty |
| | | Username cannot be empty |
| admin | Admin123 | Invalid credentials |
@ForgetPassword
Scenario: Verify Forget Password Functionality
Given User is on HRMLogin page "https://opensource-demo.orangehrmlive.com/"
When User clicks on Forgot your password link
Then User should be able to navigate to new page of title "Forgot Your Password?"
Step 3: Create extent.properties file in src/test/resources
We need to create the extent.properties file at the src/test/resources folder for the grasshopper extent report adapter to recognize it. Using a property file for reporting is quite helpful if you want to define several different properties.
Let’s enable spark report in an extent properties file:
extent.reporter.spark.start=true
extent.reporter.spark.out=Reports/Spark.html
#FolderName
basefolder.name=ExtentReports/SparkReport_
basefolder.datetimepattern=d_MMM_YY HH_mm_ss
#Screenshot
screenshot.dir=/Screenshots/
screenshot.rel.path=../Screenshots/
Step 4: Create a Helper class in src/main/java
We have used Page Object Model with Cucumber and TestNG.
Create a Helper class where we are initializing the web driver, initializing the web driver wait, defining the timeouts, and creating a private constructor of the class, it will declare the web driver, so whenever we create an object of this class, a new web browser is invoked. We are using a setter and getter method to get the object of Chromedriver with the help of a private constructor itself within the same class.
HelperClass
import java.time.Duration;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
import io.github.bonigarcia.wdm.WebDriverManager;
public class HelperClass {
private static HelperClass helperClass;
private static WebDriver driver;
private static WebDriverWait wait;
public final static int TIMEOUT = 10;
private HelperClass() {
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
wait = new WebDriverWait(driver, Duration.ofSeconds(TIMEOUT));
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(TIMEOUT));
driver.manage().window().maximize();
}
public static void openPage(String url) {
driver.get(url);
}
public static WebDriver getDriver() {
return driver;
}
public static void setUpDriver() {
if (helperClass==null) {
helperClass = new HelperClass();
}
}
public static void tearDown() {
if(driver!=null) {
driver.close();
driver.quit();
}
helperClass = null;
}
}
Step 5: Create Locator classes in src/main/java
Create a locator class for each page that contains the detail of the locators of all the web elements. Here, I’m creating 3 locator classes – LoginPageLocators, HomePageLocators, and ForgetPasswordPageLocators.
LoginPageLocators
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class LoginPageLocators {
@FindBy(name = "txtUsername")
public WebElement userName;
@FindBy(name = "txtPassword")
public WebElement password;
@FindBy(id = "logInPanelHeading")
public WebElement titleText;
@FindBy(id = "btnLogin")
public WebElement login;
@FindBy(id = "spanMessage")
public WebElement errorMessage;
@FindBy(xpath = "//*[@id='forgotPasswordLink']/a")
public WebElement forgotPasswordLink;
}
HomePageLocators
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class HomePageLocators {
@FindBy(id = "welcome")
public WebElement homePageUserName;
}
ForgetPasswordPageLocators
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class ForgetPasswordPageLocators {
@FindBy(xpath = "//*[@id='content']/div[1]/div[2]/h1")
public WebElement forgotPasswordPageHeading;
}
Step 6: Create Action classes in src/main/java
Create the action classes for each web page. These action classes contain all the methods needed by the step definitions. In this case, I have created 3 action classes – LoginPageActions, HomePageActions, and ForgetPasswordPageActions.
LoginPageActions
In this class, the very first thing will do is to create the object of LoginPageLocators class so that we should be able to access all the PageFactory elements. Secondly, create a public constructor of LoginPageActions class.
import org.openqa.selenium.support.PageFactory;
import com.example.junit.locators.LoginPageLocators;
import com.example.junit.utils.HelperClass;
public class LoginPageActions {
LoginPageLocators loginPageLocators = null;
public LoginPageActions() {
this.loginPageLocators = new LoginPageLocators();
PageFactory.initElements(HelperClass.getDriver(),loginPageLocators);
}
// Set user name in textbox
public void setUserName(String strUserName) {
loginPageLocators.userName.sendKeys(strUserName);
}
// Set password in password textbox
public void setPassword(String strPassword) {
loginPageLocators.password.sendKeys(strPassword);
}
// Click on login button
public void clickLogin() {
loginPageLocators.login.click();
}
// Get the title of Login Page
public String getLoginTitle() {
return loginPageLocators.titleText.getText();
}
// Get the title of Login Page
public String getErrorMessage() {
return loginPageLocators.errorMessage.getText();
}
// Click on forgotYourPassword Link
public void clickOnForgotPasswordLink() {
loginPageLocators.forgotPasswordLink.click();
}
public void login(String strUserName, String strPassword) {
// Fill user name
this.setUserName(strUserName);
// Fill password
this.setPassword(strPassword);
// Click Login button
this.clickLogin();
}
}
HomePageActions
import org.openqa.selenium.support.PageFactory;
import com.example.junit.locators.HomePageLocators;
import com.example.junit.utils.HelperClass;
public class HomePageActions {
HomePageLocators homePageLocators = null;
public HomePageActions() {
this.homePageLocators = new HomePageLocators();
PageFactory.initElements(HelperClass.getDriver(),homePageLocators);
}
// Get the User name from Home Page
public String getHomePageText() {
return homePageLocators.homePageUserName.getText();
}
}
ForgetPasswordPageActions
import org.openqa.selenium.support.PageFactory;
import com.example.junit.locators.ForgetPasswordPageLocators;
import com.example.junit.utils.HelperClass;
public class ForgetPasswordPageActions {
ForgetPasswordPageLocators forgetPasswordPageLocators = null;
public ForgetPasswordPageActions() {
this.forgetPasswordPageLocators = new ForgetPasswordPageLocators();
PageFactory.initElements(HelperClass.getDriver(), forgetPasswordPageLocators);
}
public String getHeading() {
return forgetPasswordPageLocators.forgotPasswordPageHeading.getText();
}
}
Step 7: Create Step Definition file in src/test/java
Create the corresponding Step Definition file of the feature file.
LoginPageDefinitions
import org.junit.Assert;
import com.example.junit.actions.ForgetPasswordPageActions;
import com.example.junit.actions.HomePageActions;
import com.example.junit.actions.LoginPageActions;
import com.example.junit.utils.HelperClass;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
public class LoginPageDefinitions{
LoginPageActions objLogin = new LoginPageActions();
HomePageActions objHomePage = new HomePageActions();
ForgetPasswordPageActions objForgotPasswordPage = new ForgetPasswordPageActions();
@Given("User is on HRMLogin page {string}")
public void loginTest(String url) {
HelperClass.openPage(url);
}
@When("User enters username as {string} and password as {string}")
public void goToHomePage(String userName, String passWord) {
// login to application
objLogin.login(userName, passWord);
// go the next page
}
@When("User clicks on Forgot your password link")
public void clickOnForgotPasswordLink() {
objLogin.clickOnForgotPasswordLink();
}
@Then("User should be able to login successfully and new page open")
public void verifyLogin() {
// Verify home page
Assert.assertTrue(objHomePage.getHomePageText().contains("Welcome"));
}
@Then("User should be able to see error message {string}")
public void verifyErrorMessage(String expectedErrorMessage) {
// Verify home page
Assert.assertEquals(objLogin.getErrorMessage(),expectedErrorMessage);
}
@Then("User should be able to navigate to new page of title {string}")
public void verifyForgotPasswordPage(String heading) {
Assert.assertEquals(objForgotPasswordPage.getHeading(),heading);
}
}
Step 8: Create Hook class in src/test/java
Create the hook class that contains the Before and After hook. @Before hook contains the method to call the setup driver which will initialize the chrome driver. This will be run before any test.
After Hook – Here will call the tearDown method.
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import com.example.junit.utils.HelperClass;
import io.cucumber.java.After;
import io.cucumber.java.Before;
import io.cucumber.java.Scenario;
public class Hooks {
@Before
public static void setUp() {
HelperClass.setUpDriver();
}
@After
public static void tearDown(Scenario scenario) {
//validate if scenario has failed
if(scenario.isFailed()) {
final byte[] screenshot = ((TakesScreenshot) HelperClass.getDriver()).getScreenshotAs(OutputType.BYTES);
scenario.attach(screenshot, "image/png", scenario.getName());
}
HelperClass.tearDown();
}
}
Step 9: Create a Cucumber Test Runner class in src/test/java
Add the extent report cucumber adapter to the runner class’s CucumberOption annotation. It is an important component of the configuration. It also ensures that the cucumber runner class recognizes and launches the extent report adapter for the cucumber. Please add the following text as a plugin to the CucumberOptions as described below.
plugin = {"com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:"}
This is how your runner class should look after being added to our project. Moreover, be sure to keep the colon “:” at the end.
import io.cucumber.junit.Cucumber;
import org.junit.runner.RunWith;
import io.cucumber.junit.CucumberOptions;
@RunWith(Cucumber.class)
@CucumberOptions(tags = "", features = "src/test/resources/features/LoginPage.feature", glue = "com.example.junit.definitions",
plugin = {"com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:"})
public class CucumberRunnerTests {
}
Step 10: Execute the code
Right Click on the Runner class and select Run As -> JUnit Test.
Below is the screenshot of the Console.

Step 11: View ExtentReport
Refresh the project and will see a new folder – Report. The ExtentReport will be present in that folder with the name Spark.html.

Right-click on Spark.html and select open with Web Browser.
The report also 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 features as shown in the image below.

Click on the first icon present on the left side of the report. To view the details about the steps, click on the scenarios. Clicking on the scenario will expand, showing off the details of the steps of each scenario. As we can see that a screenshot is attached to the failed tests here.

Congratulation!! We are able to create an Extent Report for Cucumber and JUnit4. Happy Learning!!!
How to add Screenshot to Cucumber ExtentReports
The previous tutorial explained the generation of Extent Report 5 for Cucumber and TestNG. This tutorial explains how to add screenshots to the Extent Report.
Project Structure

To set up the above project, please refer to this tutorial – ExtentReports Version 5 for Cucumber 6 and TestNG.
We want to add screenshots of failed tests to the Extent Report Version 5.
Step 1 – Add Screenshot configuration in extent.properties
extent.reporter.spark.start=true
extent.reporter.spark.out=Reports/Spark.html
#FolderName
basefolder.name=ExtentReports/SparkReport_
basefolder.datetimepattern=d_MMM_YY HH_mm_ss
#Screenshot
screenshot.dir=/Screenshots/
screenshot.rel.path=../Screenshots/
In the above example, we have provided the name “ExtentReports/SparkReport_”. It means that a folder starts with the name “SparkReport_” under the “ExtentReports” folder. The date-time pattern we have provided in another format is the basis of a valid pattern. It will concatenate with the folder name to generate a unique folder for each execution.

As seen in the image above, the “Reports” and “Screenshots” folders get created inside the new folder of SparkReports_. If we look inside the folder, we can see that the report generates.
We can browse the screenshot folder to see all the screenshots taken during each step. Additionally, screenshots will be generated and named automatically.

Step 2 – Add a method to capture the screenshot
@After
public static void tearDown(Scenario scenario) {
//validate if scenario has failed
if(scenario.isFailed()) {
final byte[] screenshot = ((TakesScreenshot) HelperClass.getDriver()).getScreenshotAs(OutputType.BYTES);
scenario.attach(screenshot, "image/png", scenario.getName());
}
In the preceding example, the tearDown() method accepts a Scenario type object. The Scenario can be found within the io.cucumber. We used Selenium’s standard screenshot feature within the method. As an example, we’d like to read the file as a byte[] type. As a parameter, the attach method accepts byte[] type objects. Scenario.attach also includes a screenshot with each step of the scenario.
The updated Hooks class will be as shown below:
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import com.example.utils.HelperClass;
import io.cucumber.java.After;
import io.cucumber.java.Before;
import io.cucumber.java.Scenario;
public class Hooks {
@Before
public static void setUp() {
HelperClass.setUpDriver();
}
@After
public static void tearDown(Scenario scenario) {
//validate if scenario has failed
if(scenario.isFailed()) {
final byte[] screenshot = ((TakesScreenshot) HelperClass.getDriver()).getScreenshotAs(OutputType.BYTES);
scenario.attach(screenshot, "image/png", scenario.getName());
}
HelperClass.tearDown();
}
}
Let’s open the report and view the report. As you can see, besides the scenario, an attachment sign is available, which means something attaches to the scenario. As we have only one failed step, only one screenshot has been captured, as seen in the above image. Right-click on Spark.html and select Open with Web Browser.

The report also 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 features as shown in the image below.

Congratulations!! We are able to attach screenshots of the failed tests. Happy Learning!!
ExtentReports Version 5 for Cucumber 6 and TestNG
What is ExtentReport?
ExtentReport is a logger-style reporting library for automated tests. ExtentReports uses the logging style to add information about test sessions, such as the creation of tests, adding screenshots, assigning tags, and adding events or series of steps to sequentially indicate the flow of test steps. ExtentReports 5 is built on an open-Core. That means, both community and professional editions use the same, full-featured API with the exception of a few reporters.
Extent Report 4 onwards, there are 2 editions of Extent Report – Core and Professional.
Below is the screenshot that shows which reporters are available in Professional or Community Editions. You can also visit this page.

This tutorial explains the use of Extent Report Core Edition.
Generation of ExtentReport 5 in Cucumber6 with TestNG
Pre-Requisite:
- Java 8 or higher is needed for ExtentReport5
- Maven or Gradle
- JAVA IDE (like Eclipse, IntelliJ, or soon)
- TestNG installed
- Cucumber Eclipse plugin (in case using Eclipse)
Project Structure

Step 1 – Add Maven dependencies to the POM
Add ExtentReport dependency
<dependency>
<groupId>com.aventstack</groupId>
<artifactId>extentreports</artifactId>
<version>5.0.9</version>
</dependency>
Add tech grasshopper maven dependency for Cucumber
<dependency>
<groupId>tech.grasshopper</groupId>
<artifactId>extentreports-cucumber6-adapter</artifactId>
<version>2.13.0</version>
</dependency>
The complete POM.xml will look like as shown below with other Selenium and TestNG dependencies.
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<cucumber.version>6.11.0</cucumber.version>
<extentreports.cucumber6.adapter.version>2.13.0</extentreports.cucumber6.adapter.version>
<extentreports.version>5.0.9</extentreports.version>
<selenium.version>4.3.0</selenium.version>
<webdrivermanager.version>5.2.1</webdrivermanager.version>
<testng.version>7.4.0</testng.version>
<maven.compiler.plugin.version>3.10.1</maven.compiler.plugin.version>
<maven.surefire.plugin.version>3.0.0-M7</maven.surefire.plugin.version>
<maven.compiler.source.version>11</maven.compiler.source.version>
<maven.compiler.target.version>11</maven.compiler.target.version>
</properties>
<dependencies>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>${cucumber.version}</version>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-testng</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>
<!-- Cucumber ExtentReport Adapter -->
<dependency>
<groupId>tech.grasshopper</groupId>
<artifactId>extentreports-cucumber6-adapter</artifactId>
<version>${extentreports.cucumber6.adapter.version}</version>
</dependency>
<!-- Extent Report -->
<dependency>
<groupId>com.aventstack</groupId>
<artifactId>extentreports</artifactId>
<version>${extentreports.version}</version>
</dependency>
<!-- Selenium -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
</dependency>
<!-- Web Driver Manager -->
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>${webdrivermanager.version}</version>
</dependency>
<!-- TestNG -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
<!-- Apache Common -->
<dependency>
<groupId>org.apache.directory.studio</groupId>
<artifactId>org.apache.commons.io</artifactId>
<version>2.4</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> <!--For JAVA 8 use 1.8-->
<target>${maven.compiler.target.version}</target> <!--For JAVA 8 use 1.8-->
</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>
Step 2: Create a feature file in src/test/resources/
Below is a sample feature file. I have also added a failed scenario in @FaceBookLink.
Feature: Login to HRM Application
@ValidCredentials
Scenario: Login with valid credentials
Given User is on HRMLogin page "https://opensource-demo.orangehrmlive.com/"
When User enters username as "Admin" and password as "admin123"
Then User should be able to login sucessfully and new page open
@InvalidCredentials
Scenario Outline: Login with invalid credentials
Given User is on HRMLogin page "https://opensource-demo.orangehrmlive.com/"
When User enters username as "<username>" and password as "<password>"
Then User should be able to see error message "<errorMessage>"
Examples:
| username | password | errorMessage |
| | abc | Username cannot be empty |
| admin | | Password cannot be empty |
| | | Username cannot be empty |
| Admin | admin12$$ | Invalid credentials |
| admin$$ | admin123 | Invalid credentials |
@FaceBookLink
Scenario: Verify FaceBook Icon on Login Page
Given User is on HRMLogin page "https://opensource-demo.orangehrmlive.com/"
Then User should be able to see FaceBook Icon
@LinkedInLink
Scenario: Verify LinkedIn Icon on Login Page
Given User is on HRMLogin page "https://opensource-demo.orangehrmlive.com/"
Then User should be able to see LinkedIn Icon
Step 3: Create extent.properties file in src/test/resources
We need to create the extent.properties file at the src/test/resources folder for the grasshopper extent report adapter to recognize it. Using a property file for reporting is quite helpful if you want to define several different properties.
Let’s enable spark report in an extent properties file:
extent.reporter.spark.start=true
extent.reporter.spark.out=Reports/Spark.html
Step 4: Create a Helper class in src/main/java
We have used Page Object Model with Cucumber and TestNG.
Create a Helper class where we are initializing the web driver, initializing the web driver wait, defining the timeouts, and creating a private constructor of the class, within it will declare the web driver, so whenever we create an object of this class, a new web browser is invoked. We are using a setter and getter method to get the object of Chromedriver with the help of a private constructor itself within the same class.
HelperClass
import java.time.Duration;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
import io.github.bonigarcia.wdm.WebDriverManager;
public class HelperClass {
private static HelperClass helperClass;
private static WebDriver driver;
private static WebDriverWait wait;
public final static int TIMEOUT = 10;
private HelperClass() {
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver();
wait = new WebDriverWait(driver, Duration.ofSeconds(TIMEOUT));
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(TIMEOUT));
driver.manage().window().maximize();
}
public static void openPage(String url) {
driver.get(url);
}
public static WebDriver getDriver() {
return driver;
}
public static void setUpDriver() {
if (helperClass==null) {
helperClass = new HelperClass();
}
}
public static void tearDown() {
if(driver!=null) {
driver.close();
driver.quit();
}
helperClass = null;
}
}
Step 5: Create Locator classes in src/main/java
Create a locator class for each page that contains the detail of the locators of all the web elements. Here, I’m creating 2 locator classes – LoginPageLocators and HomePageLocators.
LoginPageLocators
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class LoginPageLocators {
@FindBy(name = "txtUsername")
public WebElement userName;
@FindBy(name = "txtPassword")
public WebElement password;
@FindBy(id = "logInPanelHeading")
public WebElement titleText;
@FindBy(id = "btnLogin")
public WebElement login;
@FindBy(id = "spanMessage")
public WebElement errorMessage;
@FindBy(xpath = "//*[@id='social-icons']/a[1]/img")
public WebElement linkedInIcon;
@FindBy(xpath = "//*[@id='social-icons']/a[6]/img") //Invalid Xpath
public WebElement faceBookIcon;
}
HomePageLocators
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class HomePageLocators {
@FindBy(id = "welcome")
public WebElement homePageUserName;
}
Step 6: Create Action classes in src/main/java
Create the action classes for each web page. These action classes contain all the methods needed by the step definitions. In this case, I have created 2 action classes – LoginPageActions and HomePageActions
LoginPageActions
In this class, the very first thing will do is to create the object of LoginPageLocators class so that we should be able to access all the PageFactory elements. Secondly, create a public constructor of LoginPageActions class
import org.openqa.selenium.support.PageFactory;
import com.example.locators.LoginPageLocators;
import com.example.utils.HelperClass;
public class LoginPageActions {
LoginPageLocators loginPageLocators = null;
public LoginPageActions() {
this.loginPageLocators = new LoginPageLocators();
PageFactory.initElements(HelperClass.getDriver(),loginPageLocators);
}
// Set user name in textbox
public void setUserName(String strUserName) {
loginPageLocators.userName.sendKeys(strUserName);
}
// Set password in password textbox
public void setPassword(String strPassword) {
loginPageLocators.password.sendKeys(strPassword);
}
// Click on login button
public void clickLogin() {
loginPageLocators.login.click();
}
// Get the title of Login Page
public String getLoginTitle() {
return loginPageLocators.titleText.getText();
}
// Get the title of Login Page
public String getErrorMessage() {
return loginPageLocators.errorMessage.getText();
}
// LinkedIn Icon is displayed
public Boolean getLinkedInIcon() {
return loginPageLocators.linkedInIcon.isDisplayed();
}
// FaceBook Icon is displayed
public Boolean getFaceBookIcon() {
return loginPageLocators.faceBookIcon.isDisplayed();
}
public void login(String strUserName, String strPassword) {
// Fill user name
this.setUserName(strUserName);
// Fill password
this.setPassword(strPassword);
// Click Login button
this.clickLogin();
}
}
HomePageActions
import org.openqa.selenium.support.PageFactory;
import com.example.locators.HomePageLocators;
import com.example.utils.HelperClass;
public class HomePageActions {
HomePageLocators homePageLocators = null;
public HomePageActions() {
this.homePageLocators = new HomePageLocators();
PageFactory.initElements(HelperClass.getDriver(),homePageLocators);
}
// Get the User name from Home Page
public String getHomePageText() {
return homePageLocators.homePageUserName.getText();
}
}
Step 7: Create a Step Definition file in src/test/java
Create the corresponding Step Definition file of the feature file.
LoginPageDefinitions
import org.testng.Assert;
import com.example.actions.HomePageActions;
import com.example.actions.LoginPageActions;
import com.example.utils.HelperClass;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
public class LoginPageDefinitions{
LoginPageActions objLogin = new LoginPageActions();
HomePageActions objHomePage = new HomePageActions();
@Given("User is on HRMLogin page {string}")
public void loginTest(String url) {
HelperClass.openPage(url);
}
@When("User enters username as {string} and password as {string}")
public void goToHomePage(String userName, String passWord) {
// login to application
objLogin.login(userName, passWord);
// go the next page
}
@Then("User should be able to login sucessfully and new page open")
public void verifyLogin() {
// Verify home page
Assert.assertTrue(objHomePage.getHomePageText().contains("Welcome"));
}
@Then("User should be able to see error message {string}")
public void verifyErrorMessage(String expectedErrorMessage) {
// Verify home page
Assert.assertEquals(objLogin.getErrorMessage(),expectedErrorMessage);
}
@Then("User should be able to see LinkedIn Icon")
public void verifyLinkedInIcon( ) {
Assert.assertTrue(objLogin.getLinkedInIcon());
}
@Then("User should be able to see FaceBook Icon")
public void verifyFaceBookIcon( ) {
Assert.assertTrue(objLogin.getFaceBookIcon());
}
}
Step 8: Create Hook class in src/test/java
Create the hook class that contains the Before and After hook. @Before hook contains the method to call the setup driver which will initialize the chrome driver. This will be run before any test.
After Hook – Here will call the tearDown method.
import com.example.utils.HelperClass;
import io.cucumber.java.After;
import io.cucumber.java.Before;
public class Hooks {
@Before
public static void setUp() {
HelperClass.setUpDriver();
}
@After
public static void tearDown() {
HelperClass.tearDown();
}
}
Step 9: Create a Cucumber Test Runner class in src/test/java
Add the extent report cucumber adapter to the runner class’s CucumberOption annotation. It is an important component of the configuration. It also ensures that the cucumber runner class recognizes and launches the extent report adapter for cucumber. Please add the following text as a plugin to the CucumberOptions as described below.
plugin = {"com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:"})
This is how your runner class should look after being added to our project. Moreover, be sure to keep the colon “:” at the end.
import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
@CucumberOptions(tags = "", features = "src/test/resources/features/LoginPage.feature", glue = "com.example.definitions",
plugin = {"com.aventstack.extentreports.cucumber.adapter.ExtentCucumberAdapter:"})
public class CucumberRunnerTests extends AbstractTestNGCucumberTests {
}
Step 10: Create the testng.xml for the project
Right-click on the project and select TestNG -> convert to TestNG.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite">
<test name="ExtentReport5 for Cucumber">
<classes>
<class name = "com.example.runner.CucumberRunnerTests"/>
</classes>
</test> <!-- Test -->
</suite> <!-- Suite -->
Step 11: Execute the code
Right-Click on the Runner class and select Run As -> TestNG Test.
Below is the screenshot of Console. As expected, 7 tests, out of 8 are passed and 1 is failed.

Step 12: View ExtentReport
Refresh the project and will see a new folder – Report. The ExtentReport will be present in that folder with the name Spark.html.

Right-click and open with Web Browser.
The report also 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 features as shown in the image below.

Click on the first icon present on the left side of the report. To view the details about the steps, click on the scenarios. Clicking on the scenario will expand, showing off the details of the steps of each scenario.

Step 13: How to customize the report folder name
We learned how to generate an ExtentReport in Cucumber Junit in the previous section. The problem with the previous approach is that it will continue to override the previous report once the new report is created. Typically, we must keep a backup of all the reports generated by previous tests. To accomplish this, we must save each report with a unique report name or folder name.
It’s simple to create reports with different folder names using the Extent reporter plugin adapter. Two settings must be added to our extent. basefolder.name and basefolder.datetimepattern are properties files. The values assigned to these will be combined to form a folder name. As a result, a report will be generated within that. The basefolder.datetimepattern value must be in a valid date-time format.
Let us update the extent.properties file.
extent.reporter.spark.start=true
extent.reporter.spark.out=Spark.html
#FolderName
basefolder.name=Reports/SparkReport
basefolder.datetimepattern=d-MMM-YY HH-mm-ss
The value for basefolder.name in the preceding snippet is “Report/SparkReport.” It means that the folder will be named SparkReport, and that it will create a Report folder within the project directory. You can specify the location of your folder. In the following setting, we’ve used a date and time stamp to create unique folder names by concatenating them with the report name.
So, when we run the report, it will generate at the location shown in the image below:

Congratulation!! We are able to create an Extent Report for Cucumber. Happy Learning!!!