ScreenshotNeo

BlogHow-to

How to Generate Extent Reports with Selenium

Add ExtentReports to a Selenium Java project, record real test outcomes, and reliably produce an HTML report with useful failure details.

By the ScreenshotNeo team4 October 20269 min read

To generate an Extent report with Selenium in Java, add the com.aventstack:extentreports dependency, attach an ExtentSparkReporter to one ExtentReports instance, create an ExtentTest for each test, record the outcome your test framework actually observed, and call flush() after the run. The example below produces an HTML file at target/Spark.html. ExtentReports records test information; Selenium performs browser actions, and your test framework still owns assertions and test results.

This guide covers Java. ExtentReports’ version 5 guide documents its Java library and Spark reporter; the Java code here does not apply unchanged to Python, JavaScript, or C#. Read the official ExtentReports Java documentation.

1. Add the dependencies

The research for this article found ExtentReports 5.1.2 on Maven Central. Dependency versions change, so check the Maven Central version listing before adopting this version. Selenium and ExtentReports are independent dependencies; choose versions compatible with your Java runtime, test framework, and project. Selenium’s official release page lists Java 4.49.0 as released September 9, 2026; check its official downloads page for current artifacts.

Maven

<dependencies>
  <dependency>
    <groupId>com.aventstack</groupId>
    <artifactId>extentreports</artifactId>
    <version>5.1.2</version>
  </dependency>
  <dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>4.49.0</version>
  </dependency>
</dependencies>

If your project already manages Selenium through a parent POM, BOM, or version property, retain that setup rather than duplicating the dependency. Add your test framework dependency separately if it is not already present.

Gradle

dependencies {
    testImplementation 'com.aventstack:extentreports:5.1.2'
    testImplementation 'org.seleniumhq.selenium:selenium-java:4.49.0'
}

Use implementation instead of testImplementation only if production code genuinely needs these libraries. Pin versions through your normal dependency-management mechanism and review compatibility before upgrading.

2. Create and write a Spark HTML report

This minimal Java program demonstrates the reporting lifecycle and runs a Selenium browser check. It uses Chrome and Selenium Manager’s normal driver resolution behavior. Run it in an environment with Chrome installed and permitted to launch. The unconditional pass in a minimal illustration is deliberately avoided: a passing log is written only after the assertion succeeds, and a failed test receives its exception detail before the exception is rethrown.

import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.ExtentTest;
import com.aventstack.extentreports.reporter.ExtentSparkReporter;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

import java.nio.file.Files;
import java.nio.file.Path;

public class SeleniumExtentExample {
    public static void main(String[] args) throws Exception {
        Path reportPath = Path.of("target", "Spark.html");
        Files.createDirectories(reportPath.getParent());

        ExtentSparkReporter spark = new ExtentSparkReporter(reportPath.toString());
        ExtentReports extent = new ExtentReports();
        extent.attachReporter(spark);
        extent.setSystemInfo("Browser", "Chrome");
        extent.setSystemInfo("Suite", "Smoke checks");

        WebDriver driver = null;
        ExtentTest test = extent.createTest("Example page title");
        try {
            driver = new ChromeDriver();
            driver.get("https://example.com");
            String title = driver.getTitle();
            if (!title.contains("Example")) {
                throw new AssertionError("Expected page title to contain Example; got: " + title);
            }
            test.pass("Page title contained Example");
        } catch (Throwable failure) {
            test.fail(failure);
            throw failure;
        } finally {
            if (driver != null) {
                driver.quit();
            }
            extent.flush();
        }
    }
}

Compile and run it through the project’s configured Java build. With Maven, for example, save the class under the test source tree and run the test or execution setup your project uses. A plain Java main class is illustrative; Maven does not automatically run arbitrary main classes as tests. The key output is target/Spark.html. Open that file in a browser after execution.

Make cleanup robust if browser shutdown fails

In a suite, ensure both browser cleanup and report flushing happen even if teardown itself throws. Nested finally blocks make the ordering explicit:

try {
    // Run test body and record its result.
} finally {
    try {
        if (driver != null) {
            driver.quit();
        }
    } finally {
        extent.flush();
    }
}

Usually the suite owns the single ExtentReports instance and flushes once in suite teardown, rather than creating a report and flushing it for every test.

3. Connect report entries to test outcomes

Create a report entry for each test case, not each Selenium command. Log short, useful milestones and include an exception when a test fails. The reporting API does not determine whether a test passed: do not log pass before assertions finish, and do not catch an assertion exception and then let the test framework mark the test as successful.

ExtentTest test = extent.createTest("Checkout rejects expired card");
test.info("Open checkout page");
test.info("Submit an expired card");

try {
    // Selenium actions and real framework assertions go here.
    test.pass("Expired-card message was displayed");
} catch (AssertionError | RuntimeException failure) {
    test.fail(failure);
    throw failure;
}

The exact lifecycle integration depends on the test framework. A TestNG listener, JUnit extension, or a small explicit helper can create and complete entries around framework test callbacks. There is no single universal hook shared by all frameworks; keep the Extent instance at suite scope, associate one entry with each test invocation, and flush from a suite-level completion hook. For parallel tests, use a thread-safe per-test association strategy supported by your framework, and avoid sharing a mutable current-test field across worker threads.

Record status and diagnostics that help reproduce failures

  • Use stable test names that identify the behavior under test.
  • Log meaningful steps and expected results, not every low-level WebDriver call.
  • Include exception details for failures and useful context such as browser, environment, build identifier, or test data category.
  • Do not put passwords, session tokens, personal data, or secret headers into report text or screenshots.
  • Keep report output paths predictable so CI can collect the file as a build artifact.

4. Configure the HTML report

ExtentSparkReporter generates the HTML report. You can set options such as the document title and theme in Java; use options supported by the exact ExtentReports version you have pinned. The official Spark reporter documentation also describes XML and JSON configuration.

import com.aventstack.extentreports.reporter.ExtentSparkReporter;
import com.aventstack.extentreports.reporter.configuration.Theme;

ExtentSparkReporter spark = new ExtentSparkReporter("target/Spark.html");
spark.config().setDocumentTitle("UI Test Results");
spark.config().setReportName("Regression run");
spark.config().setTheme(Theme.DARK);

Attach one or more reporters to an ExtentReports instance if your workflow needs multiple destinations. Start with one Spark HTML output unless another destination has a concrete use. If you run separate processes or CI shards, plan explicitly how to preserve and combine their outputs; do not let multiple processes write the same HTML file simultaneously.

5. Add browser screenshots to failure entries

For visual evidence, capture the browser screenshot when the failure occurs and attach it to the matching report entry. ExtentReports supports screenshot attachment; choose storage and retention according to your CI artifact strategy. A local file attachment is straightforward:

import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;

if (driver instanceof TakesScreenshot) {
    Path imagePath = Path.of("target", "screenshots", "checkout-failure.png");
    Files.createDirectories(imagePath.getParent());
    byte[] png = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
    Files.write(imagePath, png);
    test.addScreenCaptureFromPath(imagePath.toString());
}

Capture before calling quit(), since a closed session cannot provide a screenshot. Use unique filenames for parallel tests, and ensure paths referenced by the HTML remain available when someone downloads the report. A report copied without its screenshot files can contain broken attachments.

Or skip the browser setup

If the part you need is a clean screenshot artifact for a test or diagnostic workflow, ScreenshotNeo offers a screenshot API and MCP server. A Selenium run and an Extent report still serve their own testing and reporting purposes; this is an alternative for obtaining the screenshot itself.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation for options and response details. Cookie banners, popups, and chat widgets are removed before the shot; bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.

Sign up free and get 1,000 screenshots a month with no card.

6. Use the same screenshot API from Python or Node.js

The ExtentReports integration above is Java-specific. These are standalone ScreenshotNeo API calls, not Python or Node.js implementations of the ExtentReports Java workflow.

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
with open("shot.webp", "wb") as image:
    image.write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));

7. Call the API with cURL for a quick capture

This request saves an image response to a file. Replace the key and target URL; keep the access key out of source control and shared logs.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Troubleshooting

Symptom Likely cause Fix
No report file appears flush() was not reached, or the output directory does not exist. Flush from suite teardown inside cleanup logic, create the parent directory, and confirm the configured path relative to the process working directory.
Report exists but is empty or missing the latest tests Entries were created after a flush, or execution ended before finalization. Flush after all entries have been recorded. Ensure suite teardown runs on both success and failure.
Every test appears as passed The code logs pass unconditionally or swallows assertion exceptions. Record pass only after assertions complete; log the failure and rethrow it so the test framework retains the failed result.
Compilation fails on reporter classes Dependency is missing, imports target an older API, or dependency versions conflict. Confirm the resolved com.aventstack:extentreports version and use ExtentSparkReporter with version 5. Old tutorials may use removed reporter classes.
Driver fails before the test runs Browser is absent, incompatible, blocked, or driver resolution cannot access the required environment. Install/configure the browser and driver according to the CI image and Selenium setup; check network and permissions. The report should still flush from teardown.
Screenshot link is broken in downloaded report The HTML was copied without the image file or uses a path unavailable on the recipient’s machine. Publish screenshots alongside the HTML as CI artifacts and use stable relative paths, or choose an attachment strategy supported by your report setup.
Parallel tests overwrite each other’s evidence They use the same report file, screenshot name, or shared current-test variable. Use unique evidence names and framework-safe per-test associations. Give each process its own report output and combine outputs deliberately if needed.
ScreenshotNeo response is not an image The request returned an error or a page verdict other than a clean capture. Check HTTP status and response headers, including X-Page-Verdict and X-Billed, then inspect the API documentation and request parameters.

Performance, reliability, and cost

  • Report overhead: create one reporting instance per test run and flush at suite completion to avoid unnecessary repeated file writes. Log concise details; large volumes of steps and embedded screenshots increase artifact size.
  • Browser time: Selenium startup, navigation, waits, and application behavior usually dominate a small report-writing lifecycle. Capture screenshots only where the evidence adds value, such as failures or selected checkpoints.
  • Reliability: make report finalization part of suite cleanup, persist report and screenshot artifacts in CI, and isolate output paths for concurrent runs. Keep the original test failure visible even if report writing also encounters an error.
  • ExtentReports cost: the cited sources describe a Java library and dependency installation; check the project’s current licensing and dependency terms before adoption. No benchmark or fixed runtime overhead is established by the sources used here.
  • ScreenshotNeo cost: its free plan includes 1,000 shots/month with no card. Paid tiers are Starter $5 for 3,000, Growth $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000. Yearly billing gives two months free; every feature is on every plan. Only clean shots are billed; bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing.

FAQ

Does ExtentReports run Selenium tests?

No. Selenium drives the browser, your test framework runs tests and assertions, and ExtentReports records and presents the information your code sends it.

Can I use the old ExtentHtmlReporter example from a tutorial?

For ExtentReports 5, use ExtentSparkReporter. The version 5 guide says ExtentHtmlReporter and ExtentLoggerReporter were removed after being deprecated in the 4.1.x series.

Where should CI publish the report?

Write it to a stable build-relative path, then configure your CI system to retain that HTML file and any screenshot files linked from it as build artifacts.

Can Python or JavaScript use this Java code?

No. This implementation targets Java. The Python and Node.js snippets above call ScreenshotNeo’s screenshot API; they do not generate ExtentReports.

Sources