Creating Extent Reports in Selenium Using the Extent API
Learn how to implement the Extent API in Selenium using TestNG to create more interactive reports, a dashboard view, emailable reports, and more.
Join the DZone community and get the full member experience.
Join For FreeThe Extent API can produce more interactive reports, a dashboard view, graphical view, capture screenshots as images in the reports, and emailable reports which can be mailed right after the unit test is completed.
The Extent API can be configured to support programming languages like Java and .Net and unit testing frameworks like JUnit, TestNG, NUnit, etc.
Implementation of Extent API in Selenium Using TestNG
To create reports with the Extent API, we need to follow the below steps.
Create a Maven project in Eclipse and add artifacts like Selenium, TestNG, and the Extent API as dependencies in the pom.xml, as shown below:
<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.selenium</groupId>
<artifactId>PageObjectModel</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.8.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.relevantcodes/extentreports -->
<dependency>
<groupId>com.relevantcodes</groupId>
<artifactId>extentreports</artifactId>
<version>2.41.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.testng/testng -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.11</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
</dependencies>
</project>
Create a listener class using ITestListener to capture the log messages in the Extent Reports when a test event occurs, as shown below. The Extent API uses the ExtentReports and ExtentTest class to generate reports
The ExtentReports object is used to generate an HTML report in the specified location. The path of the report is given as an argument in the ExtentReports constructor.
ExtentTest is initialized from the ExtentReports object by calling the lifecycle methods of reports. To start the log information in the reports, call
startTest()
. The return of startTest() is assigned to the test as a reference variable to ExtentTest.The LogStatus enum is used to add the status message based on the test execution. We have constant values in LogStatus like INFO, PASS, FAIL, SKIP, ERROR, FATAL, WARNING, and UNKNOWN. Call the status message in the respective lifecycle method to get the appropriate message in the report.
End the test reports by calling
endTest()
andflush()
from the reports to properly close the resources.
/**
* @author aswani.kumar.avilala
*/
package com.listeners;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestResult;
import com.relevantcodes.extentreports.ExtentReports;
import com.relevantcodes.extentreports.ExtentTest;
import com.relevantcodes.extentreports.LogStatus;
public class MyListener implements ITestListener {
protected static WebDriver driver;
protected static ExtentReports reports;
protected static ExtentTest test;
public void onTestStart(ITestResult result) {
System.out.println("on test start");
test = reports.startTest(result.getMethod().getMethodName());
test.log(LogStatus.INFO, result.getMethod().getMethodName() + "test is started");
}
public void onTestSuccess(ITestResult result) {
System.out.println("on test success");
test.log(LogStatus.PASS, result.getMethod().getMethodName() + "test is passed");
}
public void onTestFailure(ITestResult result) {
System.out.println("on test failure");
test.log(LogStatus.FAIL, result.getMethod().getMethodName() + "test is failed");
TakesScreenshot ts = (TakesScreenshot) driver;
File src = ts.getScreenshotAs(OutputType.FILE);
try {
FileUtils.copyFile(src, new File("C:\\images\\" + result.getMethod().getMethodName() + ".png"));
String file = test.addScreenCapture("C:\\images\\" + result.getMethod().getMethodName() + ".png");
test.log(LogStatus.FAIL, result.getMethod().getMethodName() + "test is failed", file);
test.log(LogStatus.FAIL, result.getMethod().getMethodName() + "test is failed", result.getThrowable().getMessage());
} catch (IOException e) {
e.printStackTrace();
}
}
public void onTestSkipped(ITestResult result) {
System.out.println("on test skipped");
test.log(LogStatus.SKIP, result.getMethod().getMethodName() + "test is skipped");
}
public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
System.out.println("on test sucess within percentage");
}
public void onStart(ITestContext context) {
System.out.println("on start");
driver = new ChromeDriver(); // Set the drivers path in environment variables to avoid code(System.setProperty())
reports = new ExtentReports(new SimpleDateFormat("yyyy-MM-dd hh-mm-ss-ms").format(new Date()) + "reports.html");
}
public void onFinish(ITestContext context) {
System.out.println("on finish");
driver.close();
reports.endTest(test);
reports.flush();
}
}
Design a Register Page using the Page Object Model design pattern, as shown below:
/**
* @author aswani.kumar.avilala
*/
package com.pages;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class MercuryToursRegisterPage {
WebDriver driver;
@FindBy(xpath = "//input[@name='email']") WebElement email;
@FindBy(xpath = "//input[@name='password']") WebElement password;
@FindBy(css = "input[name='confirmPassword']") WebElement confirmPassword;
@FindBy(name = "register") WebElement register;
@FindBy(linkText = "SIGN-OFF") WebElement signoff;
public MercuryToursRegisterPage(WebDriver driver) {
super();
this.driver = driver;
}
public String clickUserInfo() {
email.sendKeys("askmail29");
password.sendKeys("askmail29");
confirmPassword.sendKeys("askmail29");
register.click();
return driver.getTitle();
}
public void clickSignOff() {
signoff.click();
}
}
Design a Login Page using the Page Object Model as a design pattern, as shown below:
/**
* @author aswani.kumar.avilala
*/
package com.pages;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.How;
public class MercuryToursLoginPage {
WebDriver driver;
@FindBy(name = "userName") WebElement username;
@FindBy(how = How.NAME, using = "password") WebElement password;
@FindBy(how = How.XPATH, using = "//input[@value='Login']") WebElement signin;
@FindBy(linkText = "REGISTER") WebElement register;
public MercuryToursLoginPage(WebDriver driver) {
super();
this.driver = driver;
}
public String clickLogin() {
username.sendKeys("askmail29");
password.sendKeys("askmail29");
signin.click();
return driver.getTitle();
}
public void clickRegister() {
register.click();
}
public void loadWebPage(String url) {
driver.get(url);
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
}
}
Design a test class which contains the actual call to the object repository created in the Page classes:
/**
* @author aswani.kumar.avilala
*/
package com.tests;
import org.testng.annotations.Test;
import com.listeners.MyListener;
import com.pages.MercuryToursLoginPage;
import com.pages.MercuryToursRegisterPage;
import com.relevantcodes.extentreports.LogStatus;
import org.testng.annotations.BeforeTest;
import org.openqa.selenium.support.PageFactory;
import org.testng.Assert;
import org.testng.annotations.AfterTest;
public class TestApp extends MyListener {
MercuryToursLoginPage mlp;
MercuryToursRegisterPage mrp;
@BeforeTest
public void beforeTest(String browserName) {
mlp = PageFactory.initElements(driver, MercuryToursLoginPage.class);
mrp = PageFactory.initElements(driver, MercuryToursRegisterPage.class);
}
@AfterTest
public void afterTest() {
System.out.println("in after test");
mlp = null;
mrp = null;
}
@Test
public void testMercuryTours() {
System.out.println("in test method");
mlp.loadWebPage("http://newtours.demoaut.com");
mlp.clickRegister();
mrp.clickUserInfo();
mrp.clickSignOff();
String title = mlp.clickLogin();
Assert.assertEquals(title, "Find a Flight: Mercury Tours:");
test.log(LogStatus.INFO, "the test tours is passed");
}
}
Configure the testng.xml file with listeners and the test classes. Attach the listener class to the test in the testng.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite" parallel="tests">
<listeners>
<listener class-name="com.listeners.MyListener"></listener>
</listeners>
<test name="Test1">
<classes>
<class name="com.tests.TestApp" />
</classes>
</test>
</suite>
<!-- Suite -->
Execute the test as a suite, as shown below:
Reports are generated in the project structure with the file name as the SimpleDateFormat.html files.
When there is a failure in the test, the onTestFailure() lifecycle method is called from the listener class as it captures the screen and adds the captured image to the report, as shown below:
When all the tests are passed, the report is shown, the onTestSuccess() lifecycle method is called from the listener class, and the respective report is generated, as shown below:
We can also view the reports in the form of a dashboard, as shown below:
Opinions expressed by DZone contributors are their own.
Comments