ExtentReports Version 4 with Selenium and TestNG

HOME

What is ExtentReports?

ExtentReports is a logger-style reporting library for automated tests. ExtentReports is a library that can be used to build a customized detailed report. It can be integrated with TestNG, JUnit, etc. This report can be built in JAVA, .NET and it provides a detailed summary of each test case and each test step too in a graphical manner. Extent reports produce HTML-based documents that offer several advantages like pie charts, graphs, screenshots addition, and test summary.  ExtentReports 4 is built on an open-Core.

Generation of Extent Reports in Selenium with TestNG

In this tutorial, I will describe How to generate an ExtentReport in a Selenium with the TestNG maven project.

Extent Reports Project Structure

Implementation Steps

1. Add the dependencies to the POM.xml in the case of the Maven project.
<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <selenium.version>4.3.0</selenium.version>
        <testng.version>7.4.0</testng.version>
        <extentreports.version>4.0.0</extentreports.version>
        <webdrivermanager.version>5.2.1</webdrivermanager.version>
        <maven-surefire-plugin-version>3.0.0-M5</maven-surefire-plugin-version>
    </properties>

	<dependencies>

		<!-- Extent Report -->
		<dependency>
			<groupId>com.aventstack</groupId>
			<artifactId>extentreports</artifactId>
			<version>${extentreports.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>

		<!-- 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>
				<configuration>
					<source>11</source> <!--For JAVA 8 use 1.8-->
					<target>11</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>

2. Create ExtentManager Class

In this class, we created a createInstance() method. Also, you need to set your ExtentReports report HTML file location.

import java.io.File;
import java.io.IOException;
import java.util.Date;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.TakesScreenshot;
import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.reporter.ExtentHtmlReporter;
import com.aventstack.extentreports.reporter.configuration.Theme;
import com.example.testcases.BaseTests;
import org.openqa.selenium.OutputType;

public class ExtentManager extends BaseTests{

	private static ExtentReports extent;
	public static String screenshotName;
		
    public static ExtentReports createInstance(String fileName) {
        
    	ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(fileName);
            
        htmlReporter.config().setTheme(Theme.DARK);
        htmlReporter.config().setDocumentTitle(fileName);
        htmlReporter.config().setEncoding("utf-8");
        htmlReporter.config().setReportName(fileName);
        
        extent = new ExtentReports();
        extent.attachReporter(htmlReporter);
        extent.setSystemInfo("Release No", "22");
        extent.setSystemInfo("Environment", "QA");
        extent.setSystemInfo("Build no", "B-12673");
              
        return extent;
    }

	
	public static void captureScreenshot() {
		
		TakesScreenshot screenshot = (TakesScreenshot)driver;
	  	  
        // Call method to capture screenshot
        File src = screenshot.getScreenshotAs(OutputType.FILE);

        try
        {
        	Date d = new Date();
    		screenshotName = d.toString().replace(":", "_").replace(" ", "_") + ".jpg";  
            FileUtils.copyFile(src,new File(System.getProperty("user.dir") + "\\reports\\" + screenshotName));
            System.out.println("Successfully captured a screenshot");
       } catch (IOException e) {
           System.out.println("Exception while taking screenshot " + e.getMessage());
      }
	}
}

The ExtentHtmlReporter is used for creating an HTML file, and it accepts a file path as a parameter.

ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(fileName);

The file path represents the path in which our extent report would be generated. This is defined in ExtentListeners class.

	static Date d = new Date();
	static String fileName = "ExtentReport_" + d.toString().replace(":", "_").replace(" ", "_") + ".html";

	private static ExtentReports extent = ExtentManager.createInstance(System.getProperty("user.dir")+"\\reports\\"+fileName);

ExtentHtmlReporter is also used to customize the extent reports. It allows many configurations to be made through the config() method. Some of the configurations that can be made are described below.

htmlReporter.config().setDocumentTitle(fileName);
htmlReporter.config().setEncoding("utf-8");
htmlReporter.config().setReportName(fileName);

We have two themes – STANDARD and DARK for customizing the look and feel of our extent reports.

htmlReporter.config().setTheme(Theme.DARK);

STANDARD Look

   htmlReporter.config().setTheme(Theme.STANDARD);

captureScreenshot() is a method in ExtentTest class that attaches the captured screenshot in the Extent Report. It takes the image path where the screenshot has been captured as the parameter and attaches the screenshot to the Extent Report in Selenium.

public static void captureScreenshot() {
		
		TakesScreenshot screenshot = (TakesScreenshot)driver;
	  	  
        // Call method to capture screenshot
        File src = screenshot.getScreenshotAs(OutputType.FILE);

        try
        {
        	Date d = new Date();
    		screenshotName = d.toString().replace(":", "_").replace(" ", "_") + ".jpg";  
            FileUtils.copyFile(src,new File(System.getProperty("user.dir") + "\\reports\\" + screenshotName));
            System.out.println("Successfully captured a screenshot");
       } catch (IOException e) {
           System.out.println("Exception while taking screenshot " + e.getMessage());
      }
	}

Step 3 – Create ExtentListeners class

This class contains the action done by extent report on each step. In our tests, we implement ITestListener and use its methods. TestNG provides the @Listeners annotation, which listens to every event that occurs in a Selenium code. TestNG Listeners are activated either before the test or after the test case. It is an interface that modifies the TestNG behavior. If any event matches an event for which we want the listener to listen then it executes the code, which ultimately results in modifying the default behavior of TestNG. To know more about ITestListener, please refer to this tutorial.

import java.util.Arrays;
import java.util.Date;
import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestResult;
import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.ExtentTest;
import com.aventstack.extentreports.MediaEntityBuilder;
import com.aventstack.extentreports.Status;
import com.aventstack.extentreports.markuputils.ExtentColor;
import com.aventstack.extentreports.markuputils.Markup;
import com.aventstack.extentreports.markuputils.MarkupHelper;

public class ExtentListeners implements ITestListener {
	
	static Date d = new Date();
	static String fileName = "ExtentReport_" + d.toString().replace(":", "_").replace(" ", "_") + ".html";

	private static ExtentReports extent = ExtentManager.createInstance(System.getProperty("user.dir")+"\\reports\\"+fileName);
	
	public static ThreadLocal<ExtentTest> testReport = new ThreadLocal<ExtentTest>();
	

	public void onTestStart(ITestResult result) {
	
		ExtentTest test = extent.createTest(result.getTestClass().getName()+"     @TestCase : "+result.getMethod().getMethodName());
        testReport.set(test);
        
	}

	public void onTestSuccess(ITestResult result) {
		
		String methodName=result.getMethod().getMethodName();
		String logText="<b>"+"TEST CASE:- "+ methodName.toUpperCase()+ " - PASSED"+"</b>";		
		Markup markup = MarkupHelper.createLabel(logText, ExtentColor.GREEN);
		testReport.get().pass(markup);
		
	}

	public void onTestFailure(ITestResult result) {
		
		String excepionMessage = Arrays.toString(result.getThrowable().getStackTrace());
		testReport.get().fail("<details>" + "<summary>" + "<b>" + "<font color=" + "red>" + "Exception Occured:Click to see"
				+ "</font>" + "</b >" + "</summary>" +excepionMessage.replaceAll(",", "<br>")+"</details>"+" \n");
		
		try {

			ExtentManager.captureScreenshot();
			testReport.get().fail("<b>" + "<font color=" + "red>" + "Screenshot of failure" + "</font>" + "</b>",
					MediaEntityBuilder.createScreenCaptureFromPath(ExtentManager.screenshotName)
							.build());
		} catch (Exception e) {

		}
		
		String failureLogg="TEST CASE FAILED";
		Markup markup = MarkupHelper.createLabel(failureLogg, ExtentColor.RED);
		testReport.get().log(Status.FAIL, markup);

	}

	public void onTestSkipped(ITestResult result) {
		
		String methodName=result.getMethod().getMethodName();
		String logText="<b>"+"TEST CASE:- "+ methodName.toUpperCase()+ " - SKIPPED"+"</b>";		
		Markup markup = MarkupHelper.createLabel(logText, ExtentColor.ORANGE);
		testReport.get().skip(markup);

	}

	public void onTestFailedButWithinSuccessPercentage(ITestResult result) {

	}

	public void onStart(ITestContext context) {
	}

	public void onFinish(ITestContext context) {

		if (extent != null) {

			extent.flush();
		}
	}
}

I have defined actions for onTestStart(), onTestSuccess(), onTestFailure(), onTestSkipped() and onFinish() methods.

The ITestListener is an interface that has unimplemented methods by default and we can add lines of code within each method. So whenever a specific event occurs, the code written within that method will be executed. 

onTestFailure() is a method in which this listener will be invoked whenever the test fails. Within this method, we shall add our code to capture screenshots whenever the test case fails on execution. The screenshot of the failed test case is also embedded in the report.

onTestSuccess() is a method that is invoked once the test execution is complete and the test has been passed. We shall add the log included in the Extent Report to mark the test case as passed within this method

String methodName=result.getMethod().getMethodName();
String logText="<b>"+"TEST CASE:- "+ methodName.toUpperCase()+ " PASSED"+"</b>";		
Markup markup = MarkupHelper.createLabel(logText, ExtentColor.GREEN);
testReport.get().pass(markup);

onTestSkipped() is a method that is invoked if the test execution is skipped. This is the same as the onTestSuccess() method.

public void onTestSkipped(ITestResult result) {
		
		String methodName=result.getMethod().getMethodName();
		String logText="<b>"+"TEST CASE:- "+ methodName.toUpperCase()+ " - SKIPPED"+"</b>";		
		Markup markup = MarkupHelper.createLabel(logText, ExtentColor.ORANGE);
		testReport.get().skip(markup);

	}

4. Create the BaseTests class

This class contains the methods to initialize the browser and exit the browser after every test.

import java.time.Duration;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import io.github.bonigarcia.wdm.WebDriverManager;

public class BaseTests {

	public static WebDriver driver;
	public WebDriverWait wait;
	  
	@BeforeTest
    public void setup() throws Exception {
          
       driver = WebDriverManager.firefoxdriver().create();
       driver.get("https://opensource-demo.orangehrmlive.com/");
       wait = new WebDriverWait(driver, Duration.ofSeconds(10));
       driver.manage().window().maximize();
    }
	  
	@AfterTest
	 public  void closeBrowser() {
	    	
	   driver.close();    	
			  
	  }

5. Create the LoginPage class

This class contains the locator of all the web elements and methods needed for the testing of the page.

package com.example.testcases;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;

public class LoginPage extends BaseTests{

    WebDriver driver;

    @FindBy(name = "txtUsername")
    WebElement userName;

    @FindBy(name = "txtPassword")
    WebElement password;

    @FindBy(id = "logInPanelHeading")
    WebElement titleText;

    @FindBy(id = "btnLogin")
    WebElement login;
    
    @FindBy(id="spanMessage")
    WebElement errorMessage;
    
    @FindBy(id="forgotPasswordLink")
    WebElement forgetPasswordLink;
    
    @FindBy(xpath="//*[@id='social-icons']/a[1]/img")
    WebElement linkedInIcon;

    public LoginPage(WebDriver driver) {
          this.driver = driver;

          // This initElements method will create all WebElements
          PageFactory.initElements(driver, this);
    }

    // Set user name in textbox
    public void setUserName(String strUserName) {
          userName.sendKeys(strUserName);
    }

    // Set password in password textbox
    public void setPassword(String strPassword) {
          password.sendKeys(strPassword);
    }

    // Click on login button
    public void clickLogin() {
          login.click();
    }

    // Get the title of Login Page
    public String getLoginTitle() {
          return titleText.getText();
    }
    
    // Get the text of forgotPasswordLink
    public String getforgotPasswordLinkText() {
          return forgetPasswordLink.getText();
    }
    
    // Get the errorMessage
    public String getErrorMessage() {
          return errorMessage.getText();
    }
    
    // Verify linkedInIcon is enabled
    public Boolean isEnabledLinkedIn() {
          return linkedInIcon.isEnabled();
    }

    public void login(String strUserName, String strPasword) {

          // Fill user name
          this.setUserName(strUserName);

          // Fill password
          this.setPassword(strPasword);

          // Click Login button
          this.clickLogin();
    }
}

6. Create the LoginTests class

This class contains all the tests. As we are using, I have assigned priority to all the tests to run the tests in a specified order.

import static org.testng.Assert.assertTrue;
import org.testng.Assert;
import org.testng.SkipException;
import org.testng.annotations.Test;

public class LoginTests extends BaseTests{

    LoginPage objLogin;

    @Test(priority = 0)
    public void verifyLoginPageTitle() {

          // Create Login Page object
          objLogin = new LoginPage(driver);

          // Verify login page text
          String loginPageTitle = objLogin.getLoginTitle();
          Assert.assertTrue(loginPageTitle.contains("LOGIN Panel"));
    }
    
    @Test(priority = 1)
    public void verifyforgetPasswordLink() {

    String expectedText= objLogin.getforgotPasswordLinkText();
    Assert.assertTrue(expectedText.contains("Forgot your password?"));
    
    }
      
    @Test(priority = 2)
    public void HomeTest() {

          // login to application
          objLogin.login("Admin1", "admin1234");

         String expectedError = objLogin.getErrorMessage();

          // Verify home page
          Assert.assertTrue(expectedError.contains("Username cannot be empty"));
    }

    @Test(priority = 3)
    public void verifyLinkedIn() {

        System.out.println("Actual linkedIn Text :" + objLogin.isEnabledLinkedIn());
        assertTrue(objLogin.isEnabledLinkedIn());
        
        System.out.println("Im in skip exception");
		throw new SkipException("Skipping this exception");
    }
       
}

In this example, there are 4 tests and out of 4, 2 should pass, 1 should fail and 1 should skip.

7. Create TestNG.xml

In the TestNG.xml file, we shall add our classes and also the listener class.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Extent Report Demo">

<listeners>
     <listener class-name ="com.example.extentlisteners.ExtentListeners"/>
  </listeners>
  
  <test name="Login Tests">
    <classes>
      <class name="com.example.testcases.LoginTests"/>
    </classes>
  </test> <!-- Test -->
</suite> <!-- Suite -->

8. Execute the tests from testng.xml

Right-click on testng.xml and select Run As -> TestNG Suite.

9 Test Execution Result

The test execution result can be seen in the console.

10. Extent Report Generation

Refresh the project and will see a folder with the name of the reports present in the project.

Right-click the report and open it in your choice of browser.

Upon opening the Extent Report, you can see the summary of the tests executed.

This is the view of the dashboard in the Extent Report. This page provides a complete view of the total number of tests executed, passed tests, failed tests, the total time taken for executing the tests, and also the classification of the tests based on the category.

Extent reports produce simple and visually appealing reports. Furthermore, the HTML-based report is simple to share with other stakeholders. Extent Reports provide greater detail, allowing testers to be more effective when it comes to quickly debug software.

Congratulations!! We are able to generate an ExtentReport. Happy Learning!!

4 thoughts on “ExtentReports Version 4 with Selenium and TestNG

  1. Could you add hooks from cucumber and TestNG though they server for various reasons and check if the extent reports are working.
    It’s this dependency:

    tech.grasshopper
    extentreports-cucumber7-adapter
    1.7.0
    test

    Like

      1. Thank you!,
        I have just come across the theory part of it, let me give it a try, looking at the subject it’s based on 6 version of cucumber and I guess the same would work for 7 with a mix of hooks from testng and cucumber.

        Like

      2. The logic should be same for both Cucumber6 and 7 for Extent Report Version5. The only difference should be the way Cucumber will be used for Cucumber7.

        Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s