Take Screenshot & Generate Extent Report | POM tutorial – Part 9

Listener, Screen shot & Extent Report

 

No framework is complete without reporting feature and we will use Extent report for this purpose.  After this tutorial, you would be able to:

  • implement listener to perform certain task (for eg. take screen shot) when any desired event occurs (test execution starts, test execution failed, test execution passed, etc.)
  • create a utility which will take the screen shot of the application during any of the above events (take screen shot when a test fails, when a test is skipped etc.)
  • Generate an extent report at the end of the execution. This should also contain screen shot of desired event, primarily when a test fails

We will create separate packages and classes to implement above mentioned components. Recall from part 2 of the tutorial, packages for listener (com.appliedselenium.listeners) and utility (com.appliedselenium.utils) have already been created.

Create another package com.appliedselenium.report which will contain the class to implement Extent Report logic. 

 

Capture Screenshot

 

Under package com.appliedselenium.utils create a class takeScreenShot which will implement the code to take the screen shot.  This method will return the path where the screen shot is saved.

The method will accept a string parameter screenShotName which we will see later in the tutorial. To make every screen shot unique, we will append the file name with date and time.

This class extends BasePage since driver object variable is type casted as TakesScreenshotBelow is the code to take the screen shot and return the path where it is stored.

 

package com.appliedselenium.utils;

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 com.appliedselenium.pages.BasePage;

public class TestUtilities extends BasePage{

    public static String takeScreenShot(String screenShotName) throws IOException {
        
        //create a string variable which will be unique always
        String df = new SimpleDateFormat("yyyyMMddhhss").format(new Date());
        
        //create object variable of TakeScreenshot class  
        TakesScreenshot ts = (TakesScreenshot)driver;
        
        //create File object variable which holds the screen shot reference 
        File source = ts.getScreenshotAs(OutputType.FILE);
        
        //store the screen shot path in path variable. Here we are storing the screenshots under screenshots folder 
        String path = System.getProperty("user.dir") + "/test-output/screenshots/" + screenShotName + df + ".png";
        
        //create another File object variable which points(refer) to the above stored path variable
        File destination = new File(path);
        
        //use FileUtils class method to save the screen shot at desired path
        FileUtils.copyFile(source, destination);
        
        //return the path where the screen shot is saved 
        return path;
        
    
        
        
    }
}

 

 

Extent Report

 

Extent report is a feature rich HTML based reporting API which is widely used in the industry. Its implementation is straight forward and you only need some tweaks to make it work with any project.

Following are the main components used while implementing extent report:

  • ExtentHtmlReporter class object variable is used to create a rich standalone HTML file/report. It can be configured using config() method.
  • ExtentReports is used to provide/add information to the HTML report.

Apart from above 2 classes, we will use another class  ExtentTest in BasePage to indicate which test to include in the report. This will have impact on all the test classes and shortly we will update all of them.

Inside the reports package, create a class ExtentReporting.java which will return the ExtentReports object variable. This returned object will be received by an object variable report of type ExtentReports in BasePage class.

Below is the code to implement Extent Report API:

 

package com.appliedselenium.report;

import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.reporter.ExtentHtmlReporter;
import com.aventstack.extentreports.reporter.configuration.Theme;

public class ExtentReporting {

    // To create a rich standalone HTML file with several configurations handled
    // using config() method.
    public static ExtentHtmlReporter htmlReporter;

    // This is used to provide/add information to the HTML report
    public static ExtentReports extent;

    // method to return the ExtentReports object variable
    public static ExtentReports getExtentInstance() {
        if (extent == null) {
            htmlReporter = new ExtentHtmlReporter(
                    System.getProperty("user.dir") + "/test-output/screenshots/extentReport.html");

            extent = new ExtentReports();

            // attach the report to extent object variable
            extent.attachReporter(htmlReporter);

            // attach the desired information on the report
            extent.setSystemInfo("Host Name", "Applied Selenium");
            extent.setSystemInfo("Environment", "QA");
            extent.setSystemInfo("User Name", "Dhawal Joshi");
            htmlReporter.config().setDocumentTitle("Extent Report Result");

            // Name of the report
            htmlReporter.config().setReportName("Applied Selenium - POM");

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

        }
        return extent;
    }

}

 

Before moving to implement listeners, let us update the BasePage class.

 

Update the BasePage.java

 

Create an object variable report of type ExtentReports to receive the extent object variable from ExtentReporting class. We will also create an object variable test of type ExtentTest to indicate which test to include in the report.

Update the BasePage.java class by adding below code just above the constructor:

 

//Create instance for Logger object
    public static Logger log = Logger.getLogger("devpinoyLogger");
    
    //get the reference of ExtentReports object to instantiate HTML reporting
    public static ExtentReports report = ExtentReporting.getExtentInstance();
    
    //indicate which test to include in report
    public static ExtentTest test;
    
    // constructor of base class
    public BasePage() {

 

Update all the test classes to include them in the extent report, write below code just below the method name:

 

@Test
    
    public void isLandingPageDisplayed() {
        test = report.createTest("isLandingPageDisplayed");
        LandingPage landingPage = new LandingPage();
                        LandingPageTest.java

 

 

@Test
    public void isProductSearched() {
        test = report.createTest("isProductSearched");
                     ProductSearchPageTest.java

 

Similarly, add this line of code for rest of the test classes.

 

Implementing Listeners

 

Till this point our framework has everything – page classes, test classes, screen shot capability, reporting feature and testng for test execution. Now we need a way to accomplish:

  • test execution through TestNG
  • screen shot(s) when any error occur/ test fails
  • prepare the report with error screen shot(s) and other information

Listeners help in performing certain actions when a certain event happens. We will utilize this property of Listeners class and implement above bullet points in our framework. Also, we will see the usage of screenShotName used in takeScreenShot class.

Under listeners package, create a class TestListener.javaThis class will:

  • implement ITestListener interface as we need test’s result (pass/ fail/ skipped etc.) Implementing this interface will add some methods (we will override some of these methods), which will help in getting the above result.
  • extends BasePage class as we need test instance of ExtentTest class.

Write below code: 

 

public class TestListener extends BasePage implements ITestListener{

}

 

Mouse hover TestListener class name and select Add unimplemented methods. This should implement all the methods from ITestListener.

 

import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestResult;

public class TestListener extends BasePage implements ITestListener{

    public void onFinish(ITestContext arg0) {
        // TODO Auto-generated method stub
        
    }

    public void onStart(ITestContext arg0) {
        // TODO Auto-generated method stub
        
    }

    public void onTestFailedButWithinSuccessPercentage(ITestResult arg0) {
        // TODO Auto-generated method stub
        
    }

    public void onTestFailure(ITestResult arg0) {
        // TODO Auto-generated method stub
        
    }

    public void onTestSkipped(ITestResult arg0) {
        // TODO Auto-generated method stub
        
    }

    public void onTestStart(ITestResult arg0) {
        // TODO Auto-generated method stub
        
    }

    public void onTestSuccess(ITestResult arg0) {
        // TODO Auto-generated method stub
        
    }

}

 

Reading the method names will give you an idea when that method will be invoked. We will write our own code under onTestSuccess() and onTestFailure() methods and this code will be executed whenever a test case (test script) is pass or fail.

These methods take arg0 as an argument of type ITestResultThis arg0 will help in getting the information related to test under execution. Using this argument, we can get the test (method) name. Thatswhy we have passed screenShotName as an argument to label the screen shot for easy identification of screen shots.

Update the class with below code:

 

package com.appliedselenium.listeners;

import java.io.IOException;

import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestResult;

import com.appliedselenium.pages.BasePage;
import com.appliedselenium.utils.TestUtilities;
import com.aventstack.extentreports.Status;

public class TestListener extends BasePage implements ITestListener{

    public void onFinish(ITestContext arg0) {
        // TODO Auto-generated method stub
        
    }

    public void onStart(ITestContext arg0) {
        
        
    }

    public void onTestFailedButWithinSuccessPercentage(ITestResult arg0) {
        // TODO Auto-generated method stub
        
    }

    public void onTestFailure(ITestResult arg0) {
        try {
            
            test.log(Status.FAIL, "Failed Case is: " + arg0.getName());
            test.addScreenCaptureFromPath(TestUtilities.takeScreenShot(arg0.getName()));
            test.log(Status.FAIL, arg0.getName()+" FAIL with error " + arg0.getThrowable());
            
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        report.flush();
        
    }

    public void onTestSkipped(ITestResult arg0) {
        // TODO Auto-generated method stub
        
    }

    public void onTestStart(ITestResult arg0) {
               
         // TODO Auto-generated method stub
    } 

    public void onTestSuccess(ITestResult arg0) {
        test.log(Status.PASS, "Pass: " + arg0.getName());
        
        
    }
    
    

}

 

test.log(Status.PASS, "Pass: " + arg0.getName()); will be executed when a test succeeded. The log method from ExtentTest class will be called using the test variable. This will indicate to the Extent Report that our test is pass. arg0.getName() will fetch the test case name and log into the report.

test.log(Status.FAIL, "Failed Case is: " + arg0.getName()); is another method of ExtentTest class which is invoked when a test fails.

test.addScreenCaptureFromPath(TestUtilities.takeScreenShot(arg0.getName())); addScreenCaptureFromPath() method will look for the path of screen shot file returned by takeScreenShot() in TestUtilities class. This screen shot will be included in the report when ever any error occurs.

test.log(Status.FAIL, arg0.getName()+" FAIL with error " + arg0.getThrowable()); is used to get additional information about the error caused.

 

Implement Listeners in testng.xml

 

Listener class must be included in testng.xml in order to use them. Update the xml as following:

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">

<listeners>
    <listener class-name="com.appliedselenium.listeners.TestListener"/>
</listeners>
    <test name="Test">
        <classes>
            <class name="com.appliedselenium.tests.LandingPageTest" />
            <class name="com.appliedselenium.tests.ProductSearchPageTest" />
            <class name="com.appliedselenium.tests.SignInPageTest" />
        </classes>
    </test> <!-- Test -->
</suite> <!-- Suite -->

 

Before execution, change the productResult string in ProductSearchPageTest class and save it. 

Execute the testng.xml and above test should fail. Refresh the project and find the exentReport.html file. Open this file in any browser and it should present you with a report with all the  details.

 

extent report

Author: Dhawal Joshi

A post-graduate in MCA, ISTQB & ITIL certified QA with more than 8 years of experience in QA working with a CMMI Level 5 organization as System Analyst. I started my automation journey with HP UFT(formerly known as QTP) and for the past few years, I am using Selenium for automation. I also have experience in Android Application Development, Java, HTML, and VBScript. When I am not working, I like to spend time with my family, cooking and learning new developments in IT.

8 thoughts on “Take Screenshot & Generate Extent Report | POM tutorial – Part 9

  1. Hi Dhawal,
    I am getting image as broken link in html report. Screenshots are getting captured in the mentioned path but in report it is displaying as broken link.

  2. Hi Dhawal,
    Please let me know, how to attach the screenshot in extent Report when we doesn’t create a separate class for Extent Report, instead we initialize extent in the BaseTest class.

    Thanks,
    Nikhil N V

    1. Hi Dhawal,

      I mean the code related to Extent Reporting mechanism is written in Base Test, no separate class is created w.r.t Extent Report.
      Please let me know

      Thanks,
      Nikhil N V

      1. Hi Nikhil,

        Thank you for stopping by.
        Extent report implementation is written in class ExtentReporting and only its invokation is done in BasePage class. It is always advisable to create separate class for extent report.

        1. Thanks for the Suggestion Dhawal about creating separate class for extent report.
          Now Already we have developed the Framework and in a day or two, we have a client demo, So please let me know how to proceed.
          Also please let me know, is it BasePage or BaseTest we need to invoke from ExtentReporting Class.

          Thanks,
          Nikhil N V

          1. Only test classes should contain Test as the prefix. BasePage is a class that contains common methods that could be used by other classes as well. I am not sure how your framework is developed and what naming conventions you are using.
            However, there are many ways in which extent report can be implemented. If your extent report related code is written in a class BaseTest, then you have to invoke the object of BaseTest class in your main (Base or parent) class and then call the method used for extent report.

  3. Thanks for the Suggestion Dhawal about creating separate class for extent report.
    Now Already we have developed the Framework and in a day or two, we have a client demo, So please let me know how to proceed.
    Also please let me know, is it BasePage or BaseTest we need to invoke from ExtentReporting Class.

    Thanks,
    Nikhil N V

Leave a Reply

Your e-mail address will not be published. Required fields are marked *