Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The reliable way to attach a browser image to a TestNG HTML report is to capture it in an ITestListener‘s onTestFailure callback, save it beneath the report directory, and give your reporting library the correct relative path (or embed the image as base64). Register the listener with testng.xml or @Listeners. The listener must be able to reach the WebDriver belonging to the failing test, including the correct driver when tests run in parallel.

Use an ITestListener for failure-time screenshots

TestNG exposes two different extension points. ITestListener receives real-time events such as test start, success, skip and failure, so it is the appropriate place to capture the browser while it still exists. IReporter.generateReport(...) runs after suites complete; it can assemble a report from artifacts that were already captured, but it is normally too late to take a screenshot if teardown has quit the browser.

The sequence is:

  1. Expose the current test’s WebDriver to the listener.
  2. In onTestFailure, create a unique file under the directory that will travel with the HTML report.
  3. Call Selenium’s screenshot capability and check that the file was written.
  4. Attach the path or an embedded media object through the report implementation.
  5. Keep the image when copying or publishing the report.

A complete Java implementation with ExtentReports

The following example uses Selenium, TestNG and the ExtentReports Java API. Adjust dependency versions and package names to those installed in your project. The important design is the listener lifecycle, per-test driver storage and a path relative to the report output.

Keep one driver per test thread

import org.openqa.selenium.WebDriver;

public final class DriverStore {
    private static final ThreadLocal<WebDriver> CURRENT = new ThreadLocal<>();

    private DriverStore() {}

    public static void set(WebDriver driver) {
        CURRENT.set(driver);
    }

    public static WebDriver get() {
        WebDriver driver = CURRENT.get();
        if (driver == null) {
            throw new IllegalStateException("No WebDriver is registered for this test thread");
        }
        return driver;
    }

    public static void clear() {
        CURRENT.remove();
    }
}

A ThreadLocal prevents a parallel test from taking another test’s browser. Create and register the driver in your fixture before the test starts, and clear it only after the listener has had a chance to capture a failure.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Provide a single ExtentReports instance

import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.reporter.ExtentSparkReporter;

public final class ExtentManager {
    private static final String REPORT_FILE = "test-output/index.html";
    private static final ExtentReports EXTENT;

    static {
        ExtentSparkReporter spark = new ExtentSparkReporter(REPORT_FILE);
        EXTENT = new ExtentReports();
        EXTENT.attachReporter(spark);
    }

    private ExtentManager() {}

    public static ExtentReports get() {
        return EXTENT;
    }
}

If your installed ExtentReports release uses a different reporter class, retain the same idea: configure the HTML file first, then ensure screenshots are stored in a sibling directory such as test-output/screenshots.

Capture and attach the image in onTestFailure

import com.aventstack.extentreports.ExtentTest;
import com.aventstack.extentreports.MediaEntityBuilder;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.testng.ITestListener;
import org.testng.ITestResult;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public final class FailureScreenshotListener implements ITestListener {
    private static final DateTimeFormatter STAMP =
            DateTimeFormatter.ofPattern("yyyyMMdd-HHmmss-SSS");

    @Override
    public void onTestFailure(ITestResult result) {
        WebDriver driver;
        try {
            driver = DriverStore.get();
        } catch (RuntimeException unavailable) {
            return; // The browser was never created or was already quit.
        }

        Path reportDir = Paths.get("test-output");
        Path imageDir = reportDir.resolve("screenshots");
        String safeName = result.getTestClass().getRealClass().getSimpleName()
                + "-" + result.getMethod().getMethodName()
                + "-" + STAMP.format(LocalDateTime.now()) + ".png";
        Path image = imageDir.resolve(safeName);

        try {
            Files.createDirectories(imageDir);
            Path temporary = ((TakesScreenshot) driver)
                    .getScreenshotAs(OutputType.FILE).toPath();
            Files.copy(temporary, image, StandardCopyOption.REPLACE_EXISTING);

            String relativePath = "screenshots/" + image.getFileName();
            ExtentTest test = ExtentManager.get().createTest(
                    result.getMethod().getMethodName());
            test.fail(result.getThrowable(),
                    MediaEntityBuilder.createScreenCaptureFromPath(relativePath).build());
        } catch (IOException | RuntimeException captureError) {
            ExtentManager.get().createTest(result.getMethod().getMethodName())
                    .fail("Test failed, but the screenshot could not be captured: "
                            + captureError.getMessage());
        }
    }

    @Override
    public void onExecutionFinish(org.testng.ITestContext context) {
        ExtentManager.get().flush();
    }
}

The report API can be wired differently in a real project: many suites create an ExtentTest in onTestStart and store it in a ThreadLocal, rather than creating one in the failure callback. Do that when you need steps, retries or a single test node containing both pass and failure events. The screenshot mechanics remain the same.

Make sure teardown does not close the browser first

A common failure is a correct listener that runs after an @AfterMethod has called driver.quit(). Capture before quitting, or guard teardown so the failure callback still sees the driver.

Rank #2
Sale
Canon PIXMA TS6520 Wireless Color Inkjet Printer, Duplex Printing, Copier/Scanner, 1.42" OLED Display, Compact, White
  • Affordable Versatility - A budget-friendly all-in-one printer perfect for both home users and hybrid workers, offering exceptional value
  • Crisp, Vibrant Prints - Experience impressive print quality for both documents and photos, thanks to its 2-cartridge hybrid ink system that delivers sharp text and vivid colors
  • Effortless Setup & Use - Get started quickly with easy setup for your smartphone or computer, so you can print, scan, and copy without delay
  • Reliable Wireless Connectivity - Enjoy stable and consistent connections with dual-band Wi-Fi (2.4GHz or 5GHz), ensuring smooth printing from anywhere in your home or office
  • Scan & Copy Handling - Utilize the device’s integrated scanner for efficient scanning and copying operations
import org.openqa.selenium.WebDriver;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;

public abstract class UiTestBase {
    @BeforeMethod
    public void startBrowser() {
        WebDriver driver = createDriver();
        DriverStore.set(driver);
    }

    @AfterMethod(alwaysRun = true)
    public void stopBrowser() {
        WebDriver driver = null;
        try {
            driver = DriverStore.get();
        } catch (IllegalStateException ignored) {
            // No driver was created.
        }
        if (driver != null) {
            driver.quit();
        }
        DriverStore.clear();
    }

    protected abstract WebDriver createDriver();
}

TestNG normally invokes listener callbacks around the test lifecycle, but the exact ordering can be affected by custom listeners and fixtures. Verify the ordering in your suite; if the browser is already gone, move cleanup to a point after capture or have the fixture itself capture before quitting.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Register the listener

Suite-wide registration with testng.xml

<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="UI suite" parallel="methods" thread-count="4">
  <listeners>
    <listener class-name="com.example.FailureScreenshotListener"/>
  </listeners>
  <test name="browser tests">
    <packages>
      <package name="com.example.tests"/>
    </packages>
  </test>
</suite>

Class-level registration with @Listeners

import org.testng.annotations.Listeners;

@Listeners(FailureScreenshotListener.class)
public class CheckoutTest extends UiTestBase {
    // Test methods go here.
}

Use the XML form when every class in a suite needs the listener. Use the annotation when only selected tests should produce these artifacts.

Keep report paths valid after publishing

File-based HTML reporters generally write a reference to the image; they do not necessarily copy the image into the HTML. A report opened on your workstation may therefore break after only index.html is uploaded. Publish the entire test-output directory, preserving screenshots/ beside the HTML. If a single self-contained document is required, use the reporting library’s supported base64 media method instead of a file path, accepting the larger HTML size.

Rank #3
Sale
Canon PIXMA TS4320 – Wireless Color Inkjet Printer with Print, Copy, Scan
  • Affordable Versatility - A budget-friendly all-in-one printer perfect for both home users and hybrid workers, offering exceptional value
  • Crisp, Vibrant Prints - Experience impressive print quality for both documents and photos, thanks to its 2-cartridge hybrid ink system that delivers sharp text and vivid colors
  • Effortless Setup & Use - Get started quickly with easy setup for your smartphone or computer, so you can print, scan, and copy without delay
  • Reliable Wireless Connectivity - Enjoy stable and consistent connections with dual-band Wi-Fi (2.4GHz or 5GHz), ensuring smooth printing from anywhere in your home or office
  • Scan & Copy Handling - Utilize the device’s integrated scanner for efficient scanning and copying operations
  • Use a run directory such as test-output/2026-09-30-1420/ when CI jobs overlap.
  • Include the test class, method and a timestamp (or a UUID) in every filename.
  • Sanitize method names before using them in a path; never allow a test parameter to create ../ segments.
  • Check the generated HTML from the same location where readers will open it.

Listener, reporter or an adapter?

Approach Best use Trade-off
ITestListener Capture the live browser at failure and update a report immediately Requires reliable driver and report-object access
IReporter Post-run assembly of results and artifacts already saved Usually too late to capture a browser that teardown has closed
ExtentReports TestNG adapter Reduce custom report plumbing when its API matches your installed versions Compatibility and output behavior depend on the adapter and reporter versions

An adapter can supply listener- or reporter-style output, but it does not remove the need to retain image files or to verify that relative paths survive CI publishing.

Diagnose missing screenshots

The report shows a broken image

Usually the HTML points to a file that was not copied. Inspect the generated src or media path, then confirm that the referenced file exists relative to index.html. Upload the complete report directory, not just the HTML.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

No image is created

The driver may be absent, already quit, or not implement TakesScreenshot. Register the driver before the test, capture before teardown, and log the exception instead of silently swallowing it.

Rank #4
HP OfficeJet Pro 8125e Wireless All-in-One Color Inkjet Printer, Print, scan, Copy, ADF, Duplex Printing Best-for-Home Office, 3 Month Instant Ink Trial Included, AI-Enabled (405T6A)
  • The OfficeJet Pro 8125e is perfect for home offices printing professional-quality color documents like business documents, reports, presentations and flyers. Print speeds up to 10 ppm color, 20 ppm black
  • PERFECTLY FORMATTED PRINTS WITH HP AI – Print web pages and emails with precision—no wasted pages or awkward layouts; HP AI easily removes unwanted content, so your prints are just the way you want
  • UPGRADED FEATURES – Fast color printing, scan, copy, auto 2-sided printing, auto document feeder, and a 225-sheet input tra
  • WIRELESS PRINTING – Stay connected with our most reliable dual-band Wi-Fi, which automatically detects and resolves connection issues
  • 3 MONTHS OF INSTANT INK WITH HP+ ACTIVATION – Subscribe to Instant Ink delivery service to get ink delivered directly to your door before you run out. After 3 months, monthly fee applies unless cancelled.

Parallel tests attach the wrong browser

A static mutable driver is shared by all threads. Replace it with a thread-scoped store such as the ThreadLocal example, and keep report test nodes thread-scoped as well.

Only the first retry has an image

Retries can reuse a method name. Add the retry count, invocation number or a UUID to the filename and decide whether each attempt gets its own report node.

The screenshot is blank or incomplete

Capture after the page reaches the state that failed. If the application renders asynchronously, wait for a meaningful element or network-idle condition in the test before the assertion. A screenshot records the browser’s current viewport; use a separate full-page capability when the failure is below the fold.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Best Value
Sale
Brother Work Smart 1360 Wireless Color Inkjet All-in-One Print, Scan, Copy
  • AFFORDABLE ALL-IN-ONE FOR HOME AND HOME OFFICE: Print, copy, and scan on one compact wireless printer designed for everyday home office printing, schoolwork, documents, and reports. Produce beautiful prints for results that stand out.
  • EASY TO USE WITH CLOUD APP CONNECTIONS: Print from and scan to popular Cloud apps(2), including Google Drive, Dropbox, Box, OneDrive, and more from the simple-to-use 1.8” color display on your printer.
  • FULL-SIZE FEATURES IN A COMPACT DESIGN: This printer includes automatic duplex (2-sided) printing, a 20-sheet single-sided Automatic Document Feeder (ADF)(3), and a 150-sheet paper tray(3). Engineered to print at fast speeds of up to 16 pages per minute (ppm) in black and up to 9 ppm in color(4).
  • MULTIPLE CONNECTION OPTIONS: Connect your way. Interface with your printer on your wireless network or via USB.
  • MOBILE PRINTING MADE EASY: Go mobile with the Brother Mobile Connect app(5) that delivers easy onscreen menu navigation for printing, copying, scanning, and device management from your mobile device. Monitor your ink usage with Page Gauge to help ensure you don’t run out(6).

ExtentReports rejects the attachment call

Check the installed ExtentReports major version and its matching TestNG adapter. Method names and reporter classes differ between releases. Keep the path-based or base64 strategy supported by that exact version rather than copying an example for another release.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Operational and cost considerations

  • PNG preserves text and is usually the safest diagnostic format; large suites may need retention limits or compression.
  • Capture only on failure unless every step needs visual evidence; this keeps CI artifacts smaller.
  • Do not put credentials, tokens or personal data into screenshots. Mask sensitive fields before the assertion or use a reporting-library redaction strategy.
  • Store artifacts with the same build identifier as the report so a rerun cannot overwrite an earlier failure.
  • Test the artifact-copy step as part of CI. A perfect listener still produces a useless report if the image directory is discarded.

Or skip the browser setup

For a screenshot of a URL outside the failing Selenium session, ScreenshotNeo provides a single HTTP request. It accepts consent banners as a visitor and removes more than 60 known consent platforms, newsletter popups and chat widgets before capture. Bot checks, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and whether it was billed. Its MCP server provides take_screenshot, get_page_info and capture_pdf tools to Claude, Cursor and other MCP clients.

See the parameter list and authentication details in the ScreenshotNeo documentation.

cURL

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

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()
open("shot.webp", "wb").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 failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo includes full-page lazy-image loading, CSS-selector element capture, device and viewport controls, retina scale, PDF output, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, configurable caching, signed links, asynchronous webhooks, bulk capture and a usage API. Every feature is on every plan: 1,000 shots per month are free with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Frequently Asked Questions

Can I attach a screenshot to TestNG’s built-in index.html without ExtentReports?

Yes. The listener can save the image and write a relative link or HTML markup through a custom reporter. TestNG’s default result files do not automatically add browser screenshots, so the report-writing code is yours.

Should I use a full-page screenshot for every failure?

Not necessarily. A viewport capture is faster and smaller; choose full-page capture when the defect may be outside the visible viewport and your WebDriver/browser supports it reliably.

Where does testng-failed.xml fit?

It is TestNG’s generated rerun configuration for failed methods. It is separate from screenshot capture; retain the screenshot artifacts from the original run if the rerun changes the page state.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.