Javadoc

Click here to view javadocs.

Pro vs Community Version

Version 3 comes with 2 versios: pro and community. Differences below:

Features Community Pro
ExtentHtmlReporter - Interactive HTML report
Support for Standard and BDD Tests
Add screenshots to tests
Add screenshots to logs
Append to existing report
Offline report
Configure View Visibility
Auto-save screenshots relative to report
Auto-link screenshots to report
Create Custom Dashboard Sections
Markup Helpers
Code Block
Label
Table
Card
External Link
Modal
AnalysisStrategy
Customizable
ExtentXReporter - ExtentX Server
Support for ExtentX-Community Server
Support for ExtentX-Pro Server
ExtentEmailReporter - Emailable reports
ExtentLogger - Realtime logger
TestDisplayOrder setting
Premium Email Support
CDN Github, ExtentReports Amazon

Contributors

View this link for all active contributors.

Maven


<!-- pom.xml -->
<dependency>
    <groupId>com.aventstack</groupId>
    <artifactId>extentreports</artifactId>
    <version>3.0.6</version>
</dependency>

What's new in 3.0.3 Professional

Version 3.0.3 launched on May 18, 2017. Here is what's new:

What's new in 3.0.6

Version 3.0.6 launched on May 11, 2017. Here is what's new:

  • new commit Enabled MarkupHelper for extending, user can extend and create their own markup util
  • new commit Adds Author view
  • new (#874) Add ScenarioOutline and allow adding an additional level for its scenarios
  • fix (#726) Fix issue where test level snapshots were not parsed and appended when using setAppendExisting
  • fix (#837) Fix issue for setAppendExisting not able to parse child/node category on append
  • fix (#838) Allow filtering category when the category label is clicked
  • fix (#853) Category Filter doesn't work if we assign category to sub node

What's new in 3.0.5

Version 3.0.5 launched on April 05, 2017. Here is what's new:

  • fix (#840) Fixed issue with BugView not showing
  • fix (#841) Fixed the exception view which was not displaying the results properly

What's new in 3.0.4

Version 3.0.4 launched on March 30, 2017. Here is what's new:

  • new #756 Allow choosing an AnalysisStrategy for reports
  • new #794 Add ability to see the MongoDb's project and report id from ExtentXReporter
  • new #792 Show PASS, INFO and DEBUG status log messages for BDD style reports
  • new #791 Added a timestamp format configuration for HTMLReporter
    
        htmlReporter.config().setTimeStampFormat("mm/dd/yyyy hh:mm:ss a");
    		
  • new #819 All ExtentXReporter to upload media for logs to ExtentX server
  • new #832 Add ability to remove tests
  • fix (#811) Fixed the search text color in the Dark theme
  • fix (#815) End time on tests not being set correctly - total time taken incorrect for tests created using a listener
  • fix (#818) Fix filters for Category view
  • fix (#820) Fix styling for category and author labels
  • fix (#823) In case of long text parent test column distorted (Exception view)
  • fix (#835) Fixed the width for the first 2 columns specific to particular table instead of applying to all table
  • fix (#836) Fixed the exception view where it was showing only the last exception

Basic Usage

This section demonstrates basic methods that you will be using with this library.

Initializing Report

It is required to start and attach reporters to ExtentReports class in order to successfully generate test information. Failure to start reporters or attaching them will result in IllegalStateException when creating tests or flushing out run content to the report.


// initialize the HtmlReporter
ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter("extent.html");

// initialize ExtentXReporter
ExtentXReporter extentxReporter = new ExtentXReporter("mongodb-host", mongodb-port);

// initialize EmailReporter (pro-only)
ExtentEmailReporter emailReporter = new ExtentEmailReporter("email.html");

// intiailize the realtime logger (pro-only)
ExtentLogger logger = new ExtentLogger();

// initialize ExtentReports and attach the HtmlReporter
ExtentReports extent = new ExtentReports();

// attach only HtmlReporter
extent.attachReporter(htmlReporter);

// attach all reporters
extent.attachReporter(htmlReporter, extentxReporter, emailReporter, logger);

Writing to Report

Once your session is complete and you are ready to write all logs to the report, simply call the flush() method.

Simply call the flush() method to write or update test information to your reporter. Below is what each reporter does to write/update results:

  • ExtentHtmlReporter: builds a new report using the html template each time upon flush with the cumulative test information
  • ExtentEmailReporter pro-only: builds a new report using the email template each time upon flush with the cumulative test information
  • ExtentXReporter: updates database at each listener event
  • ExtentLogger pro-only: logs information at each listener event

HtmlReporter: Offline Report pro-only

The HtmlReporter allows creating offline report using:


ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter("extent.html");
htmlReporter.config().setCreateOfflineReport(true);

Appending to Report

ExtentHtmlReporter and ExtentXReporter allow appending test information to an existing report.

Appending to HtmlReporter


htmlReporter.setAppendExisting(true);

Appending to ExtentXReporter

If you want to append to an existing report based on its ObjectId, use:


extentXReporter.setAppendExisting(true, reportId);

// or:
extentxReporter.config().setReportObjectId(ObjectId id);
extentxReporter.config().setReportObjectId(String id);

If you want to append to an existing report based on reportName, use:


extentxReporter.setAppendExisting(true);

Note: If you have multiple reports with the same name, the append will be applied to the last-run report that matches the report-name.

Creating Tests

To create tests, use the createTest or createNode methods:

Note: it is only possible to create tests and nodes if atleast 1 repoter is attached to ExtentReports.


// creating tests
ExtentTest test = extent.createTest("MyFirstTest");
test.pass("details");

// short-hand for above
extent
    .createTest("MyFirstTest")
    .pass("details");
	
// test with description
extent
    .createTest("MyFirstTest", "Test Description")
    .pass("details");

Adding Child Tests

To add a test node as a child of another test, use the createNode method.


// creating nodes	
ExtentTest parentTest = extent.createTest("MyFirstTest");
ExtentTest childTest = parentTest.createNode("MyFirstChildTest");
childTest.pass("details");

// short-hand for above
extent
    .createTest("MyFirstTest")
    .createNode("MyFirstChildTest")
	.pass("details");
	
// node description
ExtentTest childTest = parentTest.createNode("MyFirstChildTest", "Node Description");

Create BDD-style Tests

To create cucumber/gherkin style tests, use the gherkin model or model names:

Note: When creating BDD-style tests, all your tests must be created in the same fashion. Do not mix standard tests (as shown above) with BDD. A report can have either style of tests only.


// gherkin classes

// feature
ExtentTest feature = extent.createTest("Refund item");

// scenario
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");

// using keyword names

// feature
ExtentTest feature = extent.createTest("Refund item");

// scenario
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");

Removing Tests

To remove any started test, simply call the removeTest (3.0.4+) method:


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

Setting Test Display Order pro-only

To set the order in which tests are displayed in the report, use:


// The 1st executed test appears at the top of the report
extent.setTestDisplayOrder(TestDisplayOrder.OLDEST_FIRST);

// The last executed test appears at the top of the report
extent.setTestDisplayOrder(TestDisplayOrder.NEWEST_FIRST);

Creating Log Events


ExtentTest test = extent.createTest("TestName");
test.log(Status.PASS, "pass");
// or:
test.pass("pass");

test.log(Status.FAIL, "fail");
// or:
test.fail("fail");

Logging Exceptions

To log exceptions, simply pass the exception.

Note: doing this will also enable the defect/bug tab in the report.


Exception e;
test.fail(e);

Assign Categories to Tests

You can assign categories to tests using assignCategory method:

test.assignCategory("Regression");
test.assignCategory("Regression", "ExtentAPI");
test.assignCategory("Regression", "ExtentAPI", "category-3", "cagegory-4", ..);

// while creating test
extent
    .createTest("MyFirstTest")
	.assignCategory("Regression")
    .pass("details");

Assign Authors to Tests

You can assign categories to tests using assignAuthor method:


test.assignAuthor("aventstack");
test.assignAuthor("name1", "name2");

// while creating test
extent
    .createTest("MyFirstTest")
	.assignAuthor("aventstack")
    .pass("details");

Screenshots

It is possible to attach screenshots to test and logs:


// adding screenshots to log
test.fail("details", MediaEntityBuilder.createScreenCaptureFromPath("screenshot.png").build());

// or:
MediaModelProvider mediaModel = MediaEntityBuilder.createScreenCaptureFromPath("screenshot.png").build();
test.fail("details", mediaModel);	

// adding screenshots to test
test.fail("details").addScreenCaptureFromPath("screenshot.png");

Inserting custom HTML

Simply insert any custom HTML in the logs by using an HTML tag:


extent.log(LogStatus.INFO, "HTML", "Usage: BOLD TEXT");

Adding System Info

It is possible to add system or environment info for your using using the setSystemInfo method.

Note: This method automatically adds system information to all started reporters.


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

ExtentHtmlReporter Shortcuts

The ExtentHtmlReporter supports several short-cuts for quick viewing and navigation:

Views


t - test-view
c - category-view
x - exception-view
d - dashboard

Filters


p - show passed tests
e - show error tests
f - show failed tests
s - show skipped tests
w - show warning tests
esc - clear filters

Scroll


down-arrow - scroll down
up-arrow - scroll up

ExtentHtmlReporter Features

Extent-Config.xml Professional


<?xml version="1.0" encoding="UTF-8"?>
<extentreports>
    <configuration>
        <!-- report theme -->
        <!-- standard, dark -->
        <theme>standard</theme>
    
        <!-- document encoding -->
        <!-- defaults to UTF-8 -->
        <encoding>UTF-8</encoding>
        
        <!-- protocol for script and stylesheets -->
        <!-- defaults to https -->
        <protocol>https</protocol>
        
        <!-- title of the document -->
        <documentTitle>Extent</documentTitle>
        
        <!-- report name - displayed at top-nav -->
        <reportName>Automation Report</reportName>
        
        <!-- location of charts in the test view -->
        <!-- top, bottom -->
        <testViewChartLocation>bottom</testViewChartLocation>
        
		<!-- settings to enable/disable views -->
		<!-- professional version only -->
		<enableCategoryView>true</enableCategoryView>
		<enableAuthorView>false</enableAuthorView>
		<enableExceptionView>true</enableExceptionView>
		<enableTestRunnerLogsView>true</enableTestRunnerLogsView>
		
        <!-- custom javascript -->
        <scripts>
            <![CDATA[
                $(document).ready(function() {
                    
                });
            ]]>
        </scripts>
        
        <!-- custom styles -->
        <styles>
            <![CDATA[
                
            ]]>
        </styles>
    </configuration>
</extentreports>

Extent-Config.xml Community


<?xml version="1.0" encoding="UTF-8"?>
<extentreports>
    <configuration>
        <!-- report theme -->
        <!-- standard, dark -->
        <theme>standard</theme>
    
        <!-- document encoding -->
        <!-- defaults to UTF-8 -->
        <encoding>UTF-8</encoding>
        
        <!-- protocol for script and stylesheets -->
        <!-- defaults to https -->
        <protocol>https</protocol>
        
        <!-- title of the document -->
        <documentTitle>Extent</documentTitle>
        
        <!-- report name - displayed at top-nav -->
        <reportName>Automation Report</reportName>
        
        <!-- location of charts in the test view -->
        <!-- top, bottom -->
        <testViewChartLocation>bottom</testViewChartLocation>
		
        <!-- custom javascript -->
        <scripts>
            <![CDATA[
                $(document).ready(function() {
                    
                });
            ]]>
        </scripts>
        
        <!-- custom styles -->
        <styles>
            <![CDATA[
                
            ]]>
        </styles>
    </configuration>
</extentreports>

Automatic Screenshot Management pro-only

To automatically create relative paths from the report, use configuration:


htmlReporter.config().setAutoCreateRelativePathMedia(true);

This configuration will copy the media-file to a directory relative to the file without you having to manually provide path that works relative to the report.


ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter("extent.html");
htmlReporter.config().setAutoCreateRelativePathMedia(true);

test1.fail("details", MediaEntityBuilder.createScreenCaptureFromPath("1.png").build());
test2.fail("details", MediaEntityBuilder.createScreenCaptureFromPath("2.png").build());

The above 2 screenshots will automatically be saved as and added to the report. This will allow you to move the screenshots along with the report without any side-affects, such breaking the report or failure to load screenshots on load.


extent.html
/ extent.0
    1.png
    2.png

Offline Report pro-only


htmlReporter.config().setCreateOfflineReport(true);

Append Tests to Existing Report


ExtentHtmlReporter htmlreporter = new ExtentHtmlReporter("Extent.html");
htmlreporter.setAppendExisting(true);

Configuring Analysis Strategy

It is possible to configure the analysis strategy for the report (only available in HTMLReporter). To view the differences between the different types of analysis strategies, view the dashboard/analysis section from the links below:

  • Test: (Default) 4 Tests (Child02, Child03, Child04, GrandChild1), each having 1 log (4 logs/steps)
  • Suite: 2 Suites (ParentTest01, ParentTest02), 4 Tests (Child01, Child02, Child03, Child04), 1 Test Method (GrandChild1)
  • Class: 2 Classes (ParentTest01, ParentTest02), 4 Tests (Child02, Child03, Child04, GrandChild1), Each having 1 log (4 logs/steps)

// test view (default):
extent.setAnalysisStrategy(AnalysisStrategy.TEST);

// suite view:
extent.setAnalysisStrategy(AnalysisStrategy.SUITE);

// class view:
extent.setAnalysisStrategy(AnalysisStrategy.CLASS);

Configuring View Visibility pro-only

It is possible to configure the visibility of views using config.xml or code:


// enable category view
htmlReporter.config().setCategoryViewVisibility(true);

// disable Author view
htmlReporter.config().setAuthorViewVisibility(false);

// disable TestRunnerLogs view
htmlReporter.config().setTestRunnerLogsViewVisibility(false);

// enable Exception view
htmlReporter.config().setExceptionViewVisibility(true);

The same can also be done from config.xml:


<!-- settings to enable/disable views -->
<enableCategoryView>true</enableCategoryView>
<enableAuthorView>false</enableAuthorView>
<enableExceptionView>true</enableExceptionView>
<enableTestRunnerLogsView>true</enableTestRunnerLogsView>

htmlReporter.loadXMLConfig("extent-config.xml");

Creating Custom Dashboard Sections pro-only

To add custom sections in the report, use htmlReporter.views().dashboard().setSection(...):


// setSection(String name, SectionSize size, String[] header, List data)
String[] header = new String[] { "HeaderA", "HeaderB", "HeaderC" };
        
List list = new ArrayList();
list.add(new String[] { "cell1", "cell2", "cell3" });
list.add(new String[] { "cell4", "cell5", "cell6" });

htmlReporter.views().dashboard().setSection("Sample", SectionSize.S4, header, list);

ExtentXReporter Features

Getting ReportID


extentXReporter.getReportId();

Getting ProjectID


extentXReporter.getProjectId();

Append to Existing Report


extentXReporter.setAppendExisting(true, reportId);

// or:
extentXReporter.config().setReportObjectId(id);

Append to Last Run Report pro-only

(pro version only, 3.0.2+) It is possible to automatically append your results from the current session, to the last run session in ExtentX (mongodb).


extentXReporter.appendToLastRunReport();

// or:
ObjectId reportId = extentXReporter.getLastRunReportId();
extentXReporter.setAppendExisting(true, reportId);

Be careful when using this setting when you have multiple projects. If you fail to set the project, or set the project after this setting, ExtentXReporter will by default append to the last-run report, which may belong to another project. Always remember to set the project (if you have multiple projects) before using this setting.


extentXReporter.config().setProjectName(projectName);

Markup Helpers

A few helpers are provided to allow:

  • Code block
  • Table
  • Label
  • Card pro-only
  • External Link pro-only
  • Modal pro-only

Code block


String code = "\n\t\n\t\tText\n\t\n";
Markup m = MarkupHelper.createCodeBlock(code);

test.pass(m);
// or
test.log(Status.PASS, m);

Label


String text = "extent";
Markup m = MarkupHelper.createLabel(text, ExtentColor.BLUE);

test.pass(m);
// or
test.log(Status.PASS, m);

Card pro-only


String text = "This text will become part of a material card.";
Markup m = MarkupHelper.createCard(text, ExtentColor.CYAN);

test.pass(m);
// or
test.log(Status.PASS, m);

String url = "http://extentreports.com";
String name = "extent";
Markup m = MarkupHelper.createExternalLink(url, name);

test.pass(m);
// or
test.log(Status.PASS, m);

Modal pro-only


test.info(MarkupHelper.createModal("Modal text"));

Configuration

Status Hierarchy Override Configuration

(#680) It is possible to setup your own hierarchy by using ExtentReports config():


List statusHierarchy = Arrays.asList(
                    Status.FATAL,
                    Status.FAIL,
                    Status.ERROR,
                    Status.WARNING,
                    Status.SKIP,
                    Status.PASS,
                    Status.DEBUG,
                    Status.INFO
);

extent.config().statusConfigurator().setStatusHierarchy(statusHierarchy);

Configuring Analysis Strategy

See AnalysisStrategy in ExtentHtmlReporter features section.

ExtentHtmlReporter Configuration

Each reporter supports several configuration items to change the look and feel, add content, manage tests etc.


// make the charts visible on report open
htmlReporter.config().setChartVisibilityOnOpen(true);

// create offline report (pro-only)
htmlReporter.config().setCreateOfflineReport(true);

// automatic screenshot management (pro-only)
htmlReporter.config().setAutoCreateRelativePathMedia(true);

// report title
htmlReporter.config().setDocumentTitle("aventstack - ExtentReports");

// encoding, default = UTF-8
htmlReporter.config().setEncoding("UTF-8");

// protocol (http, https)
htmlReporter.config().setProtocol(Protocol.HTTPS);

// report or build name
htmlReporter.config().setReportName("Build-1224");

// chart location - top, bottom
htmlReporter.config().setTestViewChartLocation(ChartLocation.BOTTOM);

// theme - standard, dark
htmlReporter.config().setTheme(Theme.STANDARD);

// set timeStamp format
htmlReporter.config().setTimeStampFormat("mm/dd/yyyy hh:mm:ss a");

// add custom css
htmlreporter.config().setCSS("css-string");

// add custom javascript
htmlreporter.config().setJS("js-string");

ExtentEmailReporter Configuration pro-only


// report or build name
emailReporter.config().setReportName("Today's Report");

// report title
emailReporter.config().setDocumentTitle("aventstack - ExtentReports");

// encoding, default = UTF-8
emailReporter.config().setEncoding("UTF-8");

ExtentXReporter Configuration


// project name
extentx.config().setProjectName("ProjectName");

// report or build name
extentx.config().setReportName("Build-1224");

// server URL
// ! must provide this to be able to upload snapshots
// Note: this is the address to the ExtentX server, not the Mongo database
extentx.config().setServerUrl("http://localhost:1337");

ExtentLogger Configuration pro-only

Coming soon!

Examples

This section shows basic examples to get started with ExtentReports 3.x.

Basic Usage Example

A simple example with a static entry-point.


public class Main {
    public static void main(String[] args) {
        // start reporters
        ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter("extent.html");
    
        // create ExtentReports and attach reporter(s)
        ExtentReports extent = new ExtentReports();
        extent.attachReporter(htmlReporter);

        // creates a toggle for the given test, adds all log events under it    
        ExtentTest test = extent.createTest("MyFirstTest", "Sample description");

        // log(Status, details)
        test.log(Status.INFO, "This step shows usage of log(status, details)");

        // info(details)
        test.info("This step shows usage of info(details)");
        
        // log with snapshot
        test.fail("details", MediaEntityBuilder.createScreenCaptureFromPath("screenshot.png").build());
        
        // test with snapshot
        test.addScreenCaptureFromPath("screenshot.png");
        
        // calling flush writes everything to the log file
        extent.flush();
    }
}

TestNG Examples

Using ExtentTestNGReportBuilder


public class ExtentTestNGReportBuilder {

	private static ExtentReports extent;
    private static ThreadLocal parentTest = new ThreadLocal();
    private static ThreadLocal test = new ThreadLocal();

	@BeforeSuite
	public void beforeSuite() {
		extent = ExtentManager.createInstance("extent.html");
		ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter("extent.html");
		extent.attachReporter(htmlReporter);
	}
	
    @BeforeClass
    public synchronized void beforeClass() {
        ExtentTest parent = extent.createTest(getClass().getName());
        parentTest.set(parent);
    }

    @BeforeMethod
    public synchronized void beforeMethod(Method method) {
        ExtentTest child = parentTest.get().createNode(method.getName());
        test.set(child);
    }

    @AfterMethod
    public synchronized void afterMethod(ITestResult result) {
        if (result.getStatus() == ITestResult.FAILURE)
            test.get().fail(result.getThrowable());
        else if (result.getStatus() == ITestResult.SKIP)
            test.get().skip(result.getThrowable());
        else
            test.get().pass("Test passed");

        extent.flush();
    }
}

public class ExtentManager {
    
    private static ExtentReports extent;
    
    public static ExtentReports getInstance() {
    	if (extent == null)
    		createInstance("test-output/extent.html");
    	
        return extent;
    }
    
    public static ExtentReports createInstance(String fileName) {
        ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(fileName);
        htmlReporter.config().setTestViewChartLocation(ChartLocation.BOTTOM);
        htmlReporter.config().setChartVisibilityOnOpen(true);
        htmlReporter.config().setTheme(Theme.STANDARD);
        htmlReporter.config().setDocumentTitle(fileName);
        htmlReporter.config().setEncoding("utf-8");
        htmlReporter.config().setReportName(fileName);
        
        extent = new ExtentReports();
        extent.attachReporter(htmlReporter);
        
        return extent;
    }
}

TestNG IReporter


public class ExtentTestNGIReporterListener implements IReporter {
    
    private static final String OUTPUT_FOLDER = "test-output/";
    private static final String FILE_NAME = "Extent.html";
    
    private ExtentReports extent;

    @Override
    public void generateReport(List xmlSuites, List suites, String outputDirectory) {
        init();
        
        for (ISuite suite : suites) {
            Map result = suite.getResults();
            
            for (ISuiteResult r : result.values()) {
                ITestContext context = r.getTestContext();
                
                buildTestNodes(context.getFailedTests(), Status.FAIL);
                buildTestNodes(context.getSkippedTests(), Status.SKIP);
                buildTestNodes(context.getPassedTests(), Status.PASS);
                
            }
        }
        
        for (String s : Reporter.getOutput()) {
            extent.setTestRunnerOutput(s);
        }
        
        extent.flush();
    }
    
    private void init() {
        ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(OUTPUT_FOLDER + FILE_NAME);
        htmlReporter.config().setDocumentTitle("ExtentReports - Created by TestNG Listener");
        htmlReporter.config().setReportName("ExtentReports - Created by TestNG Listener");
        htmlReporter.config().setTestViewChartLocation(ChartLocation.BOTTOM);
        htmlReporter.config().setTheme(Theme.STANDARD);
        
        extent = new ExtentReports();
        extent.attachReporter(htmlReporter);
        extent.setReportUsesManualConfiguration(true);
    }
    
    private void buildTestNodes(IResultMap tests, Status status) {
        ExtentTest test;
        
        if (tests.size() > 0) {
            for (ITestResult result : tests.getAllResults()) {
                test = extent.createTest(result.getMethod().getMethodName());
                
                for (String group : result.getMethod().getGroups())
                    test.assignCategory(group);

                if (result.getThrowable() != null) {
                    test.log(status, result.getThrowable());
                }
                else {
                    test.log(status, "Test " + status.toString().toLowerCase() + "ed");
                }
                
                test.getModel().setStartTime(getTime(result.getStartMillis()));
                test.getModel().setEndTime(getTime(result.getEndMillis()));
            }
        }
    }
    
    private Date getTime(long millis) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(millis);
        return calendar.getTime();      
    }
}

TestNG ITestListener


public class ExtentTestNGITestListener implements ITestListener {

	private static ExtentReports extent = ExtentManager.createInstance("extent.html");
	private static ThreadLocal parentTest = new ThreadLocal();
    private static ThreadLocal test = new ThreadLocal();
	
    @Override
	public synchronized void onStart(ITestContext context) {
    	ExtentTest parent = extent.createTest(getClass().getName());
        parentTest.set(parent);
	}

	@Override
	public synchronized void onFinish(ITestContext context) {
		extent.flush();
	}
	
	@Override
	public synchronized void onTestStart(ITestResult result) {
		ExtentTest child = parentTest.get().createNode(result.getMethod().getMethodName());
        test.set(child);
	}

	@Override
	public synchronized void onTestSuccess(ITestResult result) {
		test.get().pass("Test passed");
	}

	@Override
	public synchronized void onTestFailure(ITestResult result) {
		test.get().fail(result.getThrowable());
	}

	@Override
	public synchronized void onTestSkipped(ITestResult result) {
		test.get().skip(result.getThrowable());
	}

	@Override
	public synchronized void onTestFailedButWithinSuccessPercentage(ITestResult result) {
		
	}
}

public class ExtentManager {
    
    private static ExtentReports extent;
    
    public static ExtentReports getInstance() {
    	if (extent == null)
    		createInstance("test-output/extent.html");
    	
        return extent;
    }
    
    public static ExtentReports createInstance(String fileName) {
        ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(fileName);
        htmlReporter.config().setTestViewChartLocation(ChartLocation.BOTTOM);
        htmlReporter.config().setChartVisibilityOnOpen(true);
        htmlReporter.config().setTheme(Theme.STANDARD);
        htmlReporter.config().setDocumentTitle(fileName);
        htmlReporter.config().setEncoding("utf-8");
        htmlReporter.config().setReportName(fileName);
        
        extent = new ExtentReports();
        extent.attachReporter(htmlReporter);
        
        return extent;
    }
}

Sending Email using JavaMail API

Below is a quick example showing a possible way to email source to recipients using JavaMail API.

Note: this may not work in your environment. If SSL authentication is required, use "Java SMTP Example with SSL Authentication" approach as shown here.


import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class Email {
	
	// supply your SMTP host name
	private static String host = "host";
	
	// supply a static sendTo list
	private static String to = "recipient@host.com";
	
	// supply the sender email
	private static String from = "sender@host.com";
	
	/ default cc list
	private static String cc = "";
	
	// default bcc list
	private static String bcc = "";
	
	public static void send(String from, String to, String subject, String contents) throws MessagingException {		
		Properties prop = System.getProperties();
		prop.setProperty("mail.smtp.host", host);
		
		Session session = Session.getDefaultInstance(prop);
		
		MimeMessage message = new MimeMessage(session);
		message.setFrom(new InternetAddress(from));
		message.setSubject(subject);
		message.setContent(contents, "text/html");

		List toList = getAddress(to);
		for (String address : toList) {
			message.addRecipient(Message.RecipientType.TO, new InternetAddress(address));
		}
		
		List ccList = getAddress(cc);
		for (String address : ccList) {
			message.addRecipient(Message.RecipientType.CC, new InternetAddress(address));
		}
		
		List bccList = getAddress(bcc);
		for (String address : bccList) {
			message.addRecipient(Message.RecipientType.BCC, new InternetAddress(address));
		}
		
		Transport.send(message);
	}
	
	public static void send(String to, String subject, String contents) throws MessagingException {
		send(from, to, subject, contents);
	}
	
	public static void send(String subject, String contents) throws MessagingException {
		send(from, to, subject, contents);
	}
	
	private static List getAddress(String address) {
		List addressList = new ArrayList();
		
		if (address.isEmpty())
			return addressList;
		
		if (address.indexOf(";") > 0) {
			String[] addresses = address.split(";");
			
			for (String a : addresses) {
				addressList.add(a);
			}
		} else {
			addressList.add(address);
		}
		
		return addressList;
	}

}

// usage
Email.send("me@host.com", "recip1@host.com;recip2@host.com", "New Subject", "<h1>header</h2>")

License

View license for your relevant version here.