Yesterday I was asked to add some kind of reporting to our selenium test suite. I had some ideas to create a report in excel, but this all ended up in having to write a lot of code and probably reinventing the wheel. After some time using google I came across the website of Bas Dijkstra where he was reviewing ExtentReport version 1.4. Before reading the rest of my post you might want to read this post. After reading I became pretty exited about this library! Next I went to see the website of the creator of ExtentReports, ReleventCodes.com. I installed the latest version (at the time 2.04) by adding it to my maven pom.xml and gave it a go. Below my approach and some tips.
Installation:
For installation I used maven. Add the following to the pom.xml of your test project:
<dependency> <groupId>com.relevantcodes</groupId> <artifactId>extentreports</artifactId> <version>2.04</version> </dependency>
Open your command prompt and navigate to where ever your pom.xml is located and execute the following command:
mvn install -DskipTests
And your ready to go!
Using Junit? Use a @Rule annotation!
Since we already developed a “complete” automation test suite I didn’t want to add a new reporting line after every test we have written. When your experimenting with this library you could do this but later on you probably want to put this on a higher level in your testsuite.
First of all I want to tell you a little about the testsuite. I’m using Selenium webdriver (Java) and I developed our suite using the PageObjectPattern. I wrote a baseTestClass and all other tests extend this class. The baseTestClass responsibility is to setup Selenium (start a browser, navigating to a URL, truncate a database, etc.) I wrote a screenshotTaker class earlier which makes a screenshot of the browser when the test fails. This screenshotTakerClass has been added as a rule to the baseTestClass. This seems like a good starting point for me to see if a test fails if it creates a report and how it looks. For now I just added a line of code to the “failed” method of this specific class:
BaseTestClass snippet:
See line 11 for the rule where we call on the screenshotTakerClass
public class BaseTestclass { public static WebDriver driver; /* * Set some properties as a member. */ private static String screenshotLocatie; private static String browser; @Rule public ScreenshotTaker screenShootRule = new ScreenshotTaker(driver, screenshotLocatie); @BeforeClass public static void setup() throws Exception { getProperties(); //empty the database before testing. resetDatabase(); if ("Firefox".equals(browser)) { driver = new FirefoxDriver(); } else if("InternetExplorer".equals(browser)) { driver = new InternetExplorerDriver(); } else { //fallback when browser propertie is not set to firefox. driver = new FirefoxDriver(); } //set the browser to max size driver.manage().window().maximize(); } }
screenshotTakerClass:
public class ScreenshotTaker extends TestWatcher { private WebDriver browser; private String screenshotLocation; private String filenameOfReport= "C:/myReportingDir/testReport" + DateTimeHelper.getDateOfToday() + ".html"; public ScreenshotTaker(WebDriver browser, String screenshotLocatie) { this.browser = browser; this.screenshotLocation = screenshotLocation; } @Override protected void failed(Throwable e, Description description) { TakesScreenshot takesScreenshot = (TakesScreenshot) browser; File scrFile = takesScreenshot.getScreenshotAs(OutputType.FILE); File destFile = getDestinationFile(description); try { FileUtils.copyFile(scrFile, destFile); } catch (IOException ioe) { throw new RuntimeException(ioe); } ExtentReports extent = createReport(); ExtentTest test = extent.startTest(description.getDisplayName(), "Test failed, click here for further details"); // step log test.log(LogStatus.FAIL, "Failure trace Selenium: "+ e.toString()); flushReports(extent, test); } //When passed only write to the log. @Override protected void succeeded(Description description) { ExtentReports extent = createReport(); ExtentTest test = extent.startTest(description.getDisplayName(), "-"); // step log test.log(LogStatus.PASS, "-"); flushReports(extent, test); } private ExtentReports createReport() { ExtentReports extent = new ExtentReports(filenameOfReport, false); extent.config().reportName("My first extentReport report"); extent.config().reportHeadline("See my awesome passed tests!"); return extent; } private void flushReports(ExtentReports extent, ExtentTest test){ // ending test extent.endTest(test); // writing everything to document extent.flush(); } private File getDestinationFile(Description description) { String userDirectory = screenshotLocation; String date = getDateTime(); String fileName = description.getDisplayName() + "_" + date + ".png"; //add date of today String dateForDir = DateTimeHelper.getDateOfToday(); String absoluteFileName = userDirectory + "/" + dateForDir + "/" + fileName; return new File(absoluteFileName); } private String getDateTime() { Date date = Calendar.getInstance().getTime(); // Display a date in day, month, year format DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy_HH_mm_ss"); String today = formatter.format(date); return today; } }
After adding this to existing screenshotTakerClass I let JUnit run a complete test of our testsuite.
Every single test we have is added to the report, passed or failed!
Because I do not have to add a log line after every test I have to pass on the name of the test to identify the failing (or passed) test.
I came to this line (see line 23 and 34 in the code above)
ExtentTest test = extent.startTest(description.getDisplayName(), "Description of the test here....");
In the report it will show up as methodName(packageName) (for example: myAwsomeTest(com.mytestproject.seleniumtests.mostawesometests).
Next thing I wanted is to show the failure trace to see what went wrong:
I added this line:
test.log(LogStatus.FAIL, "Failure trace Selenium: "+ e.toString());
This one only applies to the failure, when a test is passed I don’t need any more information then it is passed:
test.log(LogStatus.PASS, "-");
After experimenting a little bit with ExtentReports I changed the reporting name to make it more suitable to the test project.
extent.config().reportName("Selenium - Awesome project"); extent.config().reportHeadline("Overview of testresults");
That’s it for now! After this modifications the report suited just fine for me. Only thing left is to rename the screenshotTakerClass since it now has more responsibilities.
I think EventReports is a very nice library to use when you have the need to build reports for Selenium. It will probably also fit other Java based test tools.
For more detailed information and other possibilities in Extend reports see:
http://extentreports.relevantcodes.com/2x/docs.html
Questions? Please leave a comment below!
[…] A very nice review by Richard showing the @Rule annotation to simplify reporting with Extent 2 here […]
Hi,
I am using Junit and Cucumber-JVM. I have added the @Rule in my baseTest.class. And have a screenshotTaker class, but it doesn’t seem to initiate the Extent class, so it’s throwing java.lang.nullpointer exception.
Hi,
Thnx for your reply.
Could you provide me some example of your code?
Best Regards,
Richard
Hi,
Any update on extent report for Cucumber-JVM.
Hi,
Sorry, I have no idea.
Please keep track on http://extentreports.relevantcodes.com/java/ for more information.
Its not my project 😉
I was wondering how the tests knows if it fails or passes?
HI,
Richard Kieft i am using junit and wondering how you have set up your Testsuite. will it be possible for you to share your testsuite code.
Hi,
How will i used extent report in key word-driven frame work? .
As in framework there are 3 or more step against each test cases. so will i implement this in such a way that each test case content it test steps in hierarchy way.
below piece of code return the result for test cases parent0 only. not for parent 1 and parent 2.
public static void main(String arg[])
{
ExtentReports extent=new ExtentReports(“./demo.html”);
ExtentTest parent=null;
for(int i=0;i<3;i++){
parent = extent.startTest("Parent"+i);
for(int j=i;i<3;i++){
ExtentTest child1 = extent.startTest("Child 1");
child1.log(LogStatus.INFO, "Info from method"+j);
parent
.appendChild(child1);
}
extent.endTest(parent);
extent.flush();
}
}
Please have a look.
Thank,
Puneet Jain
hi,
Thanks for the short, concise and easily digestable code on how to use EntentReports 🙂
The link to the ExtentReports documentation does not work. Tried many times.
May I have the correct url please ?
Thanks.
Jay
Hi Jay,
I think you are looking for this one:
http://extentreports.relevantcodes.com/java/
Best Regards,
Richard
well, never mind.
i found it : http://extentreports.relevantcodes.com/java/#examples
My webdriver tests are also based on the page object pattern and yes, i too use the JUnit TestWatcher class to trap the failed/succeeded tests.
I added the code for ExtentReports similar to your code and get a wonderful report.
But I did notice that the times for the tests in the report are wrong ( starttime/endtime/total time taken etc ). I am using ExtentReports v2.40.2 and JUnit 4.4.
Have you noticed that ? In your code you don’t give the test start/end time to ExtenReports. Does it get it from the JUnit framework runner automatically ?
cheers,
jay
ok. looks like one needs to set the test start/end times explicitly in the ExtentTest object in order to get the correct times in the final report.
see the testStartTime and testEndTime being set in the code below.
here is my example :
package fi.lahitapiola.henkivakuutus.junit.rule;
import java.io.File;
import java.util.Calendar;
import java.util.Date;
import java.util.Properties;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import com.cgi.testing.jay.reporting.Reporting;
import com.relevantcodes.extentreports.ExtentReports;
import com.relevantcodes.extentreports.ExtentTest;
import com.relevantcodes.extentreports.LogStatus;
import fi.lahitapiola.henkivakuutus.pageobjects.ErrorIstuntoVanhentunutPage;
import fi.lahitapiola.henkivakuutus.pageobjects.ErrorTechnicalPage;
import fi.lahitapiola.henkivakuutus.utils.PropertiesHandler;
import fi.lahitapiola.henkivakuutus.utils.Utils;
public class RikuTestWatcher extends TestWatcher {
private static String REPORT_FILENAME = “C:/temp/extentreports.html”;
private final Reporting reporting;
private final WebDriver driver;
private static boolean stopTesting = false;
private static final Properties testProps = PropertiesHandler.getTestEnvProperties();
private Date testStartTime = null;
private Date testEndTime = null;
public RikuTestWatcher( WebDriver driver ) {
super();
this.driver = driver;
reporting = new Reporting( REPORT_FILENAME, driver );
testStartTime = Calendar.getInstance().getTime();
}
public static boolean shouldStopTesting() {
return stopTesting;
}
@Override
protected void failed( Throwable t, Description desc ) {
System.out.println(“RikuTestWatcher.failed(): entered”);
System.out.println(“RikuTestWatcher.failed(): test failed: method ” + desc.getMethodName() +
“, exception class ” + t.getClass().getSimpleName() );
System.out.println(“RikuTestWatcher.failed(): exception reason: ” + t.getMessage() );
processForErrorPage( driver );
processForIstuntoVanhentunutPage( driver );
String scrShotFilename = takeScreenShot( driver );
stopTesting = true;
ExtentReports extent = reporting.createReport();
ExtentTest test = extent.startTest( desc.getDisplayName(), “Test failed, click here for further details” );
testEndTime = Calendar.getInstance().getTime();
test.setStartedTime( testStartTime );
test.setEndedTime( testEndTime );
test.log( LogStatus.FAIL, “Test method : ” + desc.getMethodName(), t.toString() );
test.log( LogStatus.FAIL, “Failed page screenshot : “, test.addScreenCapture(scrShotFilename ) );
reporting.close( extent, test );
System.out.println(“RikuTestWatcher.failed(): adding screen shot image to Extent reports :” + scrShotFilename );
System.out.println(“RikuTestWatcher.failed(): exiting”);
}
@Override
protected void succeeded( Description desc ) {
System.out.println(“RikuTestWatcher.succeeded(): entered”);
System.out.println(“RikuTestWatcher.succeeded(): test passed: method ” + desc.getMethodName() );
ExtentReports extent = reporting.createReport();
ExtentTest test = extent.startTest( desc.getDisplayName(), “-” );
testEndTime = Calendar.getInstance().getTime();
test.setStartedTime( testStartTime );
test.setEndedTime( testEndTime );
test.log( LogStatus.PASS, “Test method : ” + desc.getMethodName(), “-” );
if ( testProps != null ) {
test.log( LogStatus.PASS,
“Test method : ” + desc.getMethodName(),
“Henkivakuutus interface file : ”
);
}
reporting.close( extent, test );
System.out.println(“RikuTestWatcher.succeeded(): exiting”);
}
private void processForErrorPage( WebDriver driver ) {
ErrorTechnicalPage erPage = new ErrorTechnicalPage( driver );
erPage.displayVirheInfo();
}
private void processForIstuntoVanhentunutPage( WebDriver driver ) {
ErrorIstuntoVanhentunutPage erPage = new ErrorIstuntoVanhentunutPage( driver );
erPage.displayVirheInfo();
}
private String takeScreenShot( WebDriver driver ) {
File inScrShotFile = ( (TakesScreenshot)driver ).getScreenshotAs( OutputType.FILE );
if ( inScrShotFile == null ) {
System.out.println(“RikuTestWatcher.takeScreenShot() : webdriver failed to take screenshot of the current webpage” );
return “***”;
}
File outScrShotFile = new File( “c:\\temp\\webdriver-scrshot-” + System.currentTimeMillis() + “.png” );
String scrShotFilename = outScrShotFile.getAbsolutePath();
System.out.println(“——————————————————————————————-“);
System.out.println(“RikuTestWatcher.takeScreenShot() : writing screenshot to file : ” + scrShotFilename );
System.out.println(“——————————————————————————————-“);
Utils.copyBytes( inScrShotFile, outScrShotFile );
return scrShotFilename;
}
}
Hello,
I having an error “The method setEndedTime(Date) is undefined for the type ExtentTest” once I use following steps to get the execution time.
test.setStartedTime( testStartTime );
test.setEndedTime( testEndTime );
Hi Richard,
Can you please tell me step by step that how we can create this extent reports by using above solution ?
My tests are developed in Junit.
Hi,
ok, Do you have something like a basetestclass?
I have one and each other tests extends from this class.
Basically the basetestclass sets up everything a selenium needs, browser, database properties, login, etc.
By annotating the basetestclass with the @rule annotation all testclasses automatically will be reported in the extend test report whenever it passes or fails.
Hi Richard,
Thanks for your reply,
No , But I’m not having baseclass to operate all setting singular.
My structure of project is –
All the test scripts are separately written in junit test
This all tests are called using – one class (test suite class)
I need your help , Let me know if we can have a skype call / chat
Can’t just simply put the @rule annotation in the test suite class?
I can see a reason why it shouldn’t work….
Could you try?
I have tried in a way you have mentioned i.e. putting @rule annotation in the
class , even it has provided me result but that result does not contain test cases names it contains only testsuite name.
Can i share my code with you ?
Could you please provide me email-id where we can communicate?
Many Thanks !
Ak
Hi Richard,
Can you please provide me help here.
Akshay
Can anybody please tell me , How to implement this?
Thank you so much, this was of much help!