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, usereporter.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();
}
}