Getting Started

ExtentReports is a 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, ExtentV3HtmlReporter and ExtentLoggerReporter have been removed in version 5. The replacement is ExtentSparkReporter, which is comprehensive, ports all features along with a host of new ones.

  • ExtentReports::StartedReporters

    ExtentReports::StartedReporters 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. To detach or stop publishing messages to reporters, use reporter.OnCompleted().

  • Changes to the Status enumeration

    Status::Error, Status::Fatal, Status::Debug have been removed. The following are the current options:

    • Info
    • Pass
    • Warning
    • Skip
    • Fail

Installation

ExtentReports can be used with classic .NET setup, just like any standard .NET library as a NuGet package. Although you can copy the ExtentReports dlls, the recommended way is to use dependency management tools.

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
  • ExtentAventReporter
  • ExtentEmailReporter (available in a future release)
  • ExtentKlovReporter

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.

var 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.

var extent = new ExtentReports();
var spark = new ExtentSparkReporter("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 Debug folder of your project.


Multiple Reporters

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

var extent = new ExtentReports();
var spark = new ExtentSparkReporter("Spark.html");
var 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();

If you are using any of the reporters, simply replace them with SparkReporter:

var extent = new ExtentReports();
var avent = new ExtentAventReporter("Avent.html");
var email = new ExtentEmailReporter("Email.html");
extent.AttachReporter(avent, 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
var sparkFail = new ExtentSparkReporter("SparkFail.html")
  .Filter
    .StatusFilter
      .As(new Status[] { Status.Fail })
      .Apply();
// will contain all tests
var sparkAll = new ExtentSparkReporter("SparkAll.html");
extent.AttachReporter(sparkFail, sparkAll);

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.

var test = extent.CreateTest("MyFirstTest");
test.Pass("Pass");

// fluent
var 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.

var test = extent.CreateTest("MyFirstTest");
var node = test.CreateNode("Node");
node.Pass("Pass");

// fluent
var 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:

var feature = extent.CreateTest<Feature>("Refund item");
var scenario = feature.CreateNode<Scenario>("Jeff returns a faulty microwave");
scenario.CreateNode<Given>("Jeff has bought a microwave for $100").Pass("pass");
scenario.CreateNode<And>("he has a receipt").Pass("pass");
scenario.CreateNode<When>("he returns the microwave").Pass("pass");
scenario.CreateNode<Then>("Jeff should be refunded $100").Fail("fail");

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

var feature = extent.CreateTest(new GherkinKeyword("Feature"), "Refund item");
var scenario = feature.CreateNode(new GherkinKeyword("Scenario"), "Jeff returns a faulty microwave");
scenario.CreateNode(new GherkinKeyword("Given"), "Jeff has bought a microwave for $100").Pass("");
scenario.CreateNode(new GherkinKeyword("And"), "he has a receipt").Pass("");
scenario.CreateNode(new GherkinKeyword("When"), "he returns the microwave").Pass("");
scenario.CreateNode(new GherkinKeyword("Then"), "Jeff should be refunded $100").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.GherkinDialect = "de";

var feature = extent.CreateTest(new GherkinKeyword("Funktionalität"), "Refund item VM");
var scenario = feature.CreateNode(new GherkinKeyword("Szenario"), "Jeff returns a faulty microwave");
var 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.

var test = extent.CreateTest("Test").Fail("reason");
extent.RemoveTest(test);

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

Removing Nodes

var test = extent.CreateTest("Test");
var 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

var test = extent.CreateTest("MyFirstTest");
test.Pass("Text details");
test.Log(Status.Pass, "Text details");

Exceptions

var t = new Exception("A runtime exception");
var test = extent.CreateTest("MyFirstTest");
test.Fail(t);
test.Log(Status.Fail, t);

Screenshots

var 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.

var 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 AddSystemInfo method. This automatically adds system information to all started reporters.

extent.AddSystemInfo("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 Table {
  public string Text { get; set; } = "HelloWorld";
  public List<string> Names { get; set; } = new string[] { "Anshoo", "Extent", "Klov" }.ToList();
  public object[] Stack { get; set; } = new object[] { "Java", "C#", "Angular" };
}
extent.CreateTest("GeneratedLog")
    .GenerateLog(Status.Pass, MarkupHelper.ToTable<Table>(new Table(), "table-sm"));

Combine Multiple Reports

Starting version 5, a ExtentJsonFormatter 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.

var spark = new ExtentSparkReporter("spark.html");
var json = new ExtentJsonFormatter("extent.json");
var extent = new ExtentReports();
extent.CreateDomainFromJsonArchive("extent.json");
extent.AttachReporter(json, spark);

In the above example, a ExtentJsonFormatter 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 Table {
  public string Text { get; set; } = "HelloWorld";
  public List<string> Names { get; set; } = new string[] { "Anshoo", "Extent", "Klov" }.ToList();
  public object[] Stack { get; set; } = new object[] { "Java", "C#", "Angular" };
}

// create table as a custom log
extent.CreateTest("GeneratedLog")
  .GenerateLog(Status.Fail, MarkupHelper.ToTable<Table>(new Table(), "table-sm"));

// or as a predefined one:
extent.CreateTest("Log")
  .Fail(MarkupHelper.ToTable<Table>(new Table(), "table-sm"));

CodeBlock

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

XML

var 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>";
var 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.

var 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>";

var m = MarkupHelper.CreateCodeBlocks(new string[] { code, code });

List

Use MarkupHelper::CreateOrderedList or MarkupHelper::CreateUnorderedList to display information as ordered or unordered list.

Ordered

var items = new List<string>() { "A", "B", "C" };
extent.CreateTest("Test").Info(MarkupHelper.CreateOrderedList(items));

Unordered

var items = new HashSet<string>() { "D", "E", "F", "G" };
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.

using AventStack.ExtentReports;
using AventStack.ExtentReports.MarkupUtils;
using AventStack.ExtentReports.Reporter;

public class Program {
    private const string CODE1 = "{\n    \"theme\": \"standard\",\n    \"encoding\": \"utf-8\n}";
    private const string CODE2 = "{\n    \"protocol\": \"HTTPS\",\n    \"timelineEnabled\": false\n}";

    static void Main(string[] args) {
        var extent = new ExtentReports();
        var spark = new ExtentSparkReporter("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(new string[] { 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 Exception("A runtime exception occurred!"));

        extent.Flush();
    }
}