Getting Started

ExtentReports is an logger-style reporting library for automated tests. A logger is simply an object to log messages or events for a specific system or application. ExtentReports uses the logging style to add information about test sessions, such as creation of tests, adding screenshots, assigning tags, and adding events or series of steps to sequentially indicate the flow of test steps.


Migrating from previous versions

This document assume you are upgrading from ExtentReports 4.0 or later. The upgrade path from versions 3.0 and earlier requires taking into account many additional changes to the underlying API. Such changes are not described here and in this situation, the recommended approach is to refer to this guide for usage instructions.


Breaking Changes

If you are upgrading from version 4, the upgrade path is quite straight-forward. This document details all the breaking changes on a version 4.0 to 5.0 upgrade path. If you encounter any change other than the ones listed here, please file an issue here.

  • ExtentHtmlReporter, ExtentLoggerReporter

    The ExtentHtmlReporter and ExtentLoggerReporter were deprecated in series 4.1.x and have been removed in version 5. The replacement is ExtentSparkReporter, which is comprehensive, ports all features along with a host of new ones.

  • ExtentReports::getStartedReporters

    ExtentReports::getStartedReporters has been removed. It is no longer possible to obtain a list of started reporters via the API.

  • ExtentReports::detachReporter

    ExtentReports::detachReporter has been removed. It is no longer possible to detach observers once they have been subscribed.

  • ExtentReports:setTestRunnerOutput

    ExtentReports:setTestRunnerOutput renamed to addTestRunnerOutput. This change was made to add clarity to what this method did behind the scenes, which is adding output from test runners instead of setting it only once.

  • Changes to the Status enumeration

    Status::ERROR, Status::FATAL, Status::DEBUG have been removed to be inline with the major Java test-frameworks (JUnit and TestNG). The following are the current options:

    • Info
    • Pass
    • Warning
    • Skip
    • Fail
  • Reporter::enableTimeline

    Reporter::enableTimeline renamed to Reporter::setTimelineEnabled.


Installation: Community

ExtentReports can be used with classic Java setup, just like any standard Java library. Although you can copy the ExtentReports jars, the recommended way is to use dependency management tools such as Maven or Gradle (below). The only requirement is you need Java SDK v1.8 or higher. Before you begin, you should check the version of installed JDK using the following command:

$ java -version

Note: The package com.relevantcodes was used upto version 2. com.aventstack is the package for Versions 3+.

Maven
<dependency>
    <groupId>com.aventstack</groupId>
    <artifactId>extentreports</artifactId>
    <version>${version}</version>
</dependency>
Gradle
dependencies {
    compile "com.aventstack:extentreports:${version}"
}
GitHub

extentreports-java


Installation: Professional

Once you have completed the steps above in Installation: Community, follow the instructions included as outlined in ExtentReportsSetup.pdf available over your FTP.

Maven
<dependency>
    <groupId>com.aventstack</groupId>
    <artifactId>extentreports</artifactId>
    <version>${version}</version>
</dependency>
<dependency>
    <groupId>com.aventstack</groupId>
    <artifactId>extentreports-pro</artifactId>
    <version>${version}</version>
</dependency>
Gradle
dependencies {
    compile "com.aventstack:extentreports:${version}",
	compile "com.aventstack:extentreports-pro:${version}"
}

Reporters

Extent allows creation of tests, nodes, events and assignment of tags, devices, authors, environment values etc. This information can be printed to multiple destinations. In our context, a reporter defines the destination.

You can use one or more reporters to create different types of reports. reporters are available for BDD, non-BDD and both - choose one of the reporters from the navigation menu at the top of this page to learn more.

  • ExtentSparkReporter
    ExtentSparkReporter spark = new ExtentSparkReporter("Spark.html");
    
  • ExtentAventReporter
    ExtentAventReporter avent = new ExtentAventReporter("Avent.html");
    
  • ExtentEmailReporter (available in a future release)
    ExtentEmailReporter email = new ExtentEmailReporter("Email.html");
    
  • ExtentKlovReporter
    ExtentKlovReporter klov = new ExtentKlovReporter("ProjectName")
      .initWithDefaultSettings();
    

Usage

ExtentReports is an logger-style reporting library for automated tests. A logger is simply an object to log messages or events for a specific system or application. ExtentReports uses the logging style to add information about test sessions, such as creation of tests, adding screenshots, assigning tags, and adding events or series of steps to sequentially indicate the flow of test steps.

All methods on ExtentReports are multi-thread safe. The recommended usage is to maintain a single instance of ExtentReports object for your test session.

ExtentReports extent = new ExtentReports();

For brevity, wherever extent is mentioned, it would indicate an instance of ExtentReports.

Below is how a test would be created with 1 passing log.

extent.createTest("MyFirstTest")
  .log(Status.Pass, "This is a logging event for MyFirstTest, and it passed!");
extent.flush();

The line extent.flush() instructs ExtentReports write the test information to a destination. However, the examples above are incomplete as we need to define a destination where they will be saved. Reporters (next section) define the destination.


Reporters

The information you create during your runs can be forwarded to a variety of destinations including files, database or stored in memory to be used at a later point in time.

ExtentReports uses the Observer pattern making all reporters observers that are known by ExtentReports.

Below, an instance of ExtentSparkReporter (the Observer) is attached to ExtentReports (the Subject), which notifies ExtentSparkReporter of any changes in state such as creation of tests, adding logs etc.

ExtentReports extent = new ExtentReports();
ExtentSparkReporter spark = new ExtentSparkReporter("target/Spark.html");
extent.attachReporter(spark);
extent.createTest("MyFirstTest")
  .log(Status.PASS, "This is a logging event for MyFirstTest, and it passed!");
extent.flush();

Running the above code would produce the file Spark.html in the target folder of your project root.


Multiple Reporters

You can attach multiple reporters and output the same information to each:

ExtentReports extent = new ExtentReports();
ExtentSparkReporter spark = new ExtentSparkReporter("target/Spark.html");
ExtentKlovReporter klov = new ExtentKlovReporter("MyProject")
  .initWithDefaultSettings();
extent.attachReporter(spark, klov);
extent.createTest("MyFirstTest")
  .log(Status.PASS, "This is a logging event for MyFirstTest, and it passed!");
extent.flush();
ExtentReports extent = new ExtentReports();
ExtentSparkReporter spark = new ExtentSparkReporter("target/spark.html");
ExtentEmailReporter email = new ExtentEmailReporter("target/email.html");
extent.attachReporter(spark, email);

Filters

It is possible to create separate reports for each status (or a group of them). For example, 2 reports can be created per run session to:

  • view all tests
  • view only failed ones

The example below shows ExtentReports attaching 2 instances of ExtentSparkReporter: one adding all tests and the other only failed.

// will only contain failures
ExtentSparkReporter sparkFail = new ExtentSparkReporter("target/SparkFail.html")
  .filter()
    .statusFilter()
    .as(new Status[] { Status.FAIL })
  .apply();
// will contain all tests
ExtentSparkReporter sparkAll = new ExtentSparkReporter("spark/SparkAll.html");
extent.attachReporter(sparkFail, sparkAll);

View Order

It is also possible to select the views and their order. The example below will limit the views to only 2: Dashboard and Test. It will also make Dashboard as the primary view of the report.

Only Avent and Spark reporters support this configuration.

ExtentSparkReporter spark = new ExtentSparkReporter("spark/spark.html")
  .viewConfigurer()
    .viewOrder()
    .as(new ViewName[] { ViewName.DASHBOARD, ViewName.TEST })
  .apply();

The default setting is to display all views in this order:

  • Test (default: primary)
  • Category
  • Device
  • Author
  • Exception
  • Dashboard
  • Log

If you intend to change the view order to a view other than Test as the primary, while maintaining presence of all others, use:

ExtentSparkReporter spark = new ExtentSparkReporter("spark/spark.html")
  .viewConfigurer()
    .viewOrder()
    .as(new ViewName[] { 
	   ViewName.DASHBOARD, 
	   ViewName.TEST, 
	   ViewName.TAG, 
	   ViewName.AUTHOR, 
	   ViewName.DEVICE, 
	   ViewName.EXCEPTION, 
	   ViewName.LOG 
	})
  .apply();

Create Tests

Tests are created using ExtentReports::createTest. The createTest method returns a ExtentTest object which can be used to publish logs, create nodes, assign attributes (tags, devices, authors) or attach screenshots.

ExtentTest test = extent.createTest("MyFirstTest");
test.pass("Pass");

// fluent
ExtentTest test = extent.createTest("MyFirstTest").pass("Pass");

Nodes

ExtentReports creates the test and returns an ExtentTest object, which can create nodes using createNode. Depending on the output and reporter type, this method creates a subsection within the test, generally as a box or toggle.

ExtentTest test = extent.createTest("MyFirstTest");
ExtentTest node = test.createNode("Node");
node.pass("Pass");

// fluent
ExtentTest node = extent.createTest("MyFirstTest")
  .createNode("Node")  
  .pass("Pass");

BDD

ExtentReports support Gherkin-style reports using the same nested structure as shown in the previous section using createNode. The BDD hierarchy can be created using Gherkin classes provided by the ExtentReports library:

ExtentTest feature = extent.createTest(Feature.class, "Refund item");
ExtentTest scenario = feature.createNode(Scenario.class, "Jeff returns a faulty microwave");
scenario.createNode(Given.class, "Jeff has bought a microwave for $100").pass("pass");
scenario.createNode(And.class, "he has a receipt").pass("pass");
scenario.createNode(When.class, "he returns the microwave").pass("pass");
scenario.createNode(Then.class, "Jeff should be refunded $100").fail("fail");

The above can also be written without specifying the BDD class by passing the keyword:

ExtentTest feature = extent.createTest(new GherkinKeyword("Feature"), "Refund item");
ExtentTest scenario = feature.createNode(new GherkinKeyword("Scenario"), "Jeff returns a faulty microwave");
scenario.createNode(new GherkinKeyword("Given"), "Jeff has bought a microwave for $100").pass("pass");
scenario.createNode(new GherkinKeyword("And"), "he has a receipt").pass("pass");
scenario.createNode(new GherkinKeyword("When"), "he returns the microwave").pass("pass");
scenario.createNode(new GherkinKeyword("Then"), "Jeff should be refunded $100").fail("fail");

Gherkin Dialect

To specify the dialect of your Gherkin keywords, use setGherkinDialect. This method is helpful if you are using Gherkin keywords not supported out-of-box by ExtentReports, but are supported with Gherkin.

extent.setGherkinDialect("de");

ExtentTest feature = extent.createTest(new GherkinKeyword("Funktionalität"), "Refund item VM");
ExtentTest scenario = feature.createNode(new GherkinKeyword("Szenario"), "Jeff returns a faulty microwave");
ExtentTest given = scenario.createNode(new GherkinKeyword("Angenommen"), "Jeff has bought a microwave for $100").skip("skip");

Remove Tests

Using removeTest completely deletes the test from the run session. Note: this method will also remove all information associated with the test, including logs, screenshots, children (nodes), tags etc.

ExtentTest test = extent.createTest("Test").fail("reason");
extent.removeTest(test);

// or remove using test name
extent.removeTest("Test");

Removing Nodes

ExtentTest test = extent.createTest("Test");
ExtentTest node = test.createNode("Node").fail("reason");
extent.removeTest(node);

// or remove using test name
extent.removeTest("node");

Logging

It is possible to create log with text details, Exception/Throwable, ScreenCapture or custom Markup using MarkupHelper.

Details

ExtentTest test = extent.createTest("MyFirstTest");
test.pass("Text details");
test.log(Status.PASS, "Text details");

Exceptions

Throwable t = new RuntimeException("A runtime exception");
ExtentTest test = extent.createTest("MyFirstTest");
test.fail(t);
test.log(Status.FAIL, t);

Screenshots

ExtentTest test = extent.createTest("MyFirstTest");

// reference image saved to disk
test.fail(MediaEntityBuilder.createScreenCaptureFromPath("img.png").build());

// base64
test.fail(MediaEntityBuilder.createScreenCaptureFromBase64String("base64").build());

Markup

More information on this topic can be found in the Markup System section.

String json = "{'foo' : 'bar', 'foos' : ['b','a','r'], 'bar' : {'foo':'bar', 'bar':false,'foobar':1234}}";
test.info(MarkupHelper.createCodeBlock(json, CodeLanguage.JSON));

Attributes/Tagging

Assign Tags

You can assign tags or categories to tests using assignCategory.

test.assignCategory("tag");
test.assignCategory("tag-1", "tag-2", ..);

// usage
extent.createTest("Test").assignCategory("tag-1").pass("details");

Assigning tags enables the Tag view in BasicFileReporter reporters.


Assign Devices

You can assign devices to tests using assignDevice.

test.assignDevice("device-name");
test.assignDevice("device-1", "device-2", ..);

// usage
extent.createTest("Test").assignDevice("device-name").pass("details");

Assign Authors

You can assign categories to tests using assignAuthor.

test.assignAuthor("author");
test.assignAuthor("author-1", "author-2", ..);

// usage
extent.createTest("MyFirstTest").assignAuthor("aventstack").pass("details");

System Info

It is possible to add system or environment info for your using using the setSystemInfo method. This automatically adds system information to all started reporters.

extent.setSystemInfo("os", "macos");

Custom Logs

You can create your own custom logs, tables with custom headers, pass your objects directly to be converted into a table etc. You can also specify any CSS classes to be applied on the table, like in the below example with "table-sm" (a bootstrap table class).

public class MyCustomLog {
  private List<Object> names = Arrays.asList("Anshoo", "Extent", "Klov");
  private Object[] favStack = new Object[]{"Java", "C#", "Angular"};
  @MarkupIgnore
  private List<Object> ignored = Arrays.asList("Ignored", "Ignored", "Ignored");
  private Map<Object, Object> items = new HashMap<Object, Object>() {
      {
          put("Item1", "Value1");
          put("Item2", "Value2");
          put("Item3", "Value3");
      }
  };
}
extent.createTest("GeneratedLog")
    .generateLog(Status.PASS, MarkupHelper.toTable(new MyCustomLog(), "table-sm"));

Combine Multiple Reports

Starting version 5, a JsonFormatter is available, which uses a JSON representation of the run session to create the internal entities and combine results from multiple build sessions into one. The method createDomainFromJsonArchive is responsible for using the JSON extract to recreate entities.

ExtentSparkReporter spark = new ExtentSparkReporter("spark.html");
JsonFormatter json = new JsonFormatter("extent.json");
ExtentReports extent = new ExtentReports();
extent.createDomainFromJsonArchive("extent.json");
extent.attachReporter(json, spark);

In the above example, a JsonFormatter is created which saves the complete entity information as a JSON file to ./extent.json. The 1st time createDomainFromJsonArchive is called, no actions are performed because the file is empty. 2nd time onwards, whenever this method is called, it will read the entities and rebuild them.

The method createDomainFromJsonArchive must be called before extent.attachReporter().


Markup System

Version 5 improves upon the markup system introduced in version 4. Table and Codeblocks are vastly enhanced and List is a newly added element starting 5.0.0.


Table

Tables can be created from String[][] or a custom object (as shown below).

public class MyCustomLog {
    private List<Object> names = Arrays.asList("Anshoo", "Extent", "Klov");
    private Object[] favStack = new Object[]{"Java", "C#", "Angular"};
    @MarkupIgnore
    private List<Object> ignored = Arrays.asList("Anshoo/Ignore", "Extent/Ignore", "Klov/Ignore");
    private Map<Object, Object> items = new HashMap<Object, Object>() {
        {
            put("Item1", "Value1");
            put("Item2", "Value2");
            put("Item3", "Value3");
        }
    };
}

// create table as a custom log
extent.createTest("GeneratedLog")
  .generateLog(Status.FAIL, MarkupHelper.toTable(new MyCustomLog(), "table-sm"));

// or as a predefined one:
extent.createTest("Log")
  .fail(MarkupHelper.toTable(new MyCustomLog(), "table-sm"));

CodeBlock

CodeBlocks are helpful if you intend to display pre-formatted code.

XML

String code = "<root>" + 
"\n  <Person>" + 
"\n    <Name>Joe Doe</Name>" + 
"\n    <StartDate>2007-01-01</StartDate>" + 
"\n    <EndDate>2009-01-01</EndDate>" + 
"\n    <Location>London</Location>" + 
"\n  </Person>" + 
"\n</root>";
Markup m = MarkupHelper.createCodeBlock(code);
test.info(m);

JSON

String json = "{'foo' : 'bar', 'foos' : ['b','a','r'], 'bar' : {'foo':'bar', 'bar':false,'foobar':1234}}";
test.pass(MarkupHelper.createCodeBlock(json, CodeLanguage.JSON));

Multiple CodeBlocks

It is possible to include upto 4 code-blocks horizontally. Considering an example of a REST API test where you have a request/response, they can both be logged in a single line.

String code = "<root>" + 
"\n  <Person>" + 
"\n    <Name>Joe Doe</Name>" + 
"\n    <StartDate>2007-01-01</StartDate>" + 
"\n    <EndDate>2009-01-01</EndDate>" + 
"\n    <Location>London</Location>" + 
"\n  </Person>" + 
"\n</root>";

Markup m = MarkupHelper.createCodeBlock(code, code);

// or:
Markup m = MarkupHelper.createCodeBlocks(new String[] { code, code });

List

Use MarkupHelper::createOrderedList or MarkupHelper::createUnorderedList to display information as ordered or unordered list.

Ordered

List<Object> items = Arrays.asList(new Object[] { "Item1", "Item2", "Item3" });
extent.createTest("Test").info(MarkupHelper.createOrderedList(items));

Unordered

Map<Object, Object> items = new HashMap<Object, Object>()
{{
     put("Item1", "Value1");
     put("Item2", "Value2");
     put("Item3", "Value3");
}};
extent.createTest("Test").info(MarkupHelper.createUnorderedList(items).getMarkup());

Label

A small labeling component.

test.info(MarkupHelper.createLabel("Extent", ExtentColor.BLUE));

A Complete Example

This document shows a complete example of some of the different approaches you can use to present information.

The example is also available online here.


import com.aventstack.extentreports.markuputils.MarkupHelper;
import com.aventstack.extentreports.reporter.ExtentSparkReporter;

public class Main {
    private static final String CODE1 = "{\n    \"theme\": \"standard\",\n    \"encoding\": \"utf-8\n}";
    private static final String CODE2 = "{\n    \"protocol\": \"HTTPS\",\n    \"timelineEnabled\": false\n}";

    public static void main(String[] args) throws ClassNotFoundException {
        ExtentReports extent = new ExtentReports();
        ExtentSparkReporter spark = new ExtentSparkReporter("target/Spark/Spark.html");
        extent.attachReporter(spark);

        extent.createTest("ScreenCapture")
                .addScreenCaptureFromPath("extent.png")
                .pass(MediaEntityBuilder.createScreenCaptureFromPath("extent.png").build());

        extent.createTest("LogLevels")
                .info("info")
                .pass("pass")
                .warning("warn")
                .skip("skip")
                .fail("fail");

        extent.createTest("CodeBlock").generateLog(
                Status.PASS,
                MarkupHelper.createCodeBlock(CODE1, CODE2));

        extent.createTest("ParentWithChild")
                .createNode("Child")
                .pass("This test is created as a toggle as part of a child test of 'ParentWithChild'");

        extent.createTest("Tags")
                .assignCategory("MyTag")
                .pass("The test 'Tags' was assigned by the tag <span class='badge badge-primary'>MyTag</span>");

        extent.createTest("Authors")
                .assignAuthor("TheAuthor")
                .pass("This test 'Authors' was assigned by a special kind of author tag.");

        extent.createTest("Devices")
                .assignDevice("TheDevice")
                .pass("This test 'Devices' was assigned by a special kind of devices tag.");

        extent.createTest("Exception! <i class='fa fa-frown-o'></i>")
                .fail(new RuntimeException("A runtime exception occurred!"));

        extent.flush();
    }
}