Recommended Free Tools
Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Capture the browser with Selenium’s TakesScreenshot, save the image somewhere that the generated report can still reach, and attach it to the same ExtentTest event that records the failure. In ExtentReports 5, the reliable sequence is: create an ExtentSparkReporter, attach it to ExtentReports, create a test, call getScreenshotAs, copy the file to a stable directory, add it with MediaEntityBuilder, and call flush() in a finally block.
Complete ExtentReports 5 example
The following class shows a failure screenshot attached to the failure log. It uses a file because file paths are easy to inspect while debugging and work well when the report and image directory are packaged together.
import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.ExtentTest;
import com.aventstack.extentreports.MediaEntityBuilder;
import com.aventstack.extentreports.reporter.ExtentSparkReporter;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
public class ExtentScreenshotExample {
public static void run(WebDriver driver) throws Exception {
ExtentReports extent = new ExtentReports();
ExtentSparkReporter spark = new ExtentSparkReporter("target/Spark.html");
extent.attachReporter(spark);
ExtentTest test = extent.createTest("Login test");
try {
// Replace this with your normal Selenium test steps.
driver.get("https://example.com/login");
// An assertion or explicit check would normally detect the failure here.
throw new AssertionError("Example failure");
} catch (Throwable failure) {
File source = ((TakesScreenshot) driver)
.getScreenshotAs(OutputType.FILE);
Path destination = Path.of(
"target", "screenshots", "login-failure.png");
Files.createDirectories(destination.getParent());
Files.copy(source.toPath(), destination,
StandardCopyOption.REPLACE_EXISTING);
test.fail("Login failed: " + failure.getMessage(),
MediaEntityBuilder
.createScreenCaptureFromPath(destination.toString())
.build());
throw failure;
} finally {
extent.flush();
}
}
}
Use the Selenium dependency and the ExtentReports version declared by your build. The imports above are for the ExtentReports 5 style, where ExtentSparkReporter produces the HTML report. The cast to TakesScreenshot is valid only when the active driver implements that interface.
How the attachment pipeline works
1. Capture the current browser state
TakesScreenshot.getScreenshotAs(OutputType.FILE) asks the driver for the current view and returns a temporary file. Selenium also supports other output types, including Base64. A driver screenshot captures the rendered viewport; it is not automatically a full-page image, and it does not rewind the browser to an earlier failure state. Capture immediately after the assertion or exception that identifies the problem.
2. Move the temporary file to a controlled location
The temporary source can disappear when the test process ends. Copy it into a directory that is published with the report, such as target/screenshots. Creating the parent directory before copying avoids a common NoSuchFileException. Use a deterministic name for a single test, but include a method, parameter, timestamp, or thread identifier when tests can run concurrently.
3. Attach media to the failure event
MediaEntityBuilder.createScreenCaptureFromPath(...).build() creates media for a status or log call. Passing that entity to test.fail keeps the image beside the failure that it explains. This is preferable when a test has several statuses or checkpoints.
4. Flush after logging is complete
extent.flush() writes the current model to the HTML report. Put it in a lifecycle cleanup path so an exception during the test does not prevent the report from being written.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Two ExtentReports attachment styles
| Style | Code shape | Best use | Operational consideration |
|---|---|---|---|
| Test-level file | test.addScreenCaptureFromPath(path) |
An artifact that describes the test generally | The report stores a reference to the external image; keep that file at the recorded path. |
| Status-level file | test.fail("details", MediaEntityBuilder.createScreenCaptureFromPath(path).build()) |
A screenshot tied to one failure, warning, or log entry | Attach it to the same ExtentTest call that records the event. |
| Test-level Base64 | test.addScreenCaptureFromBase64String(base64) |
A test artifact without a separately managed image file | Image bytes stay in report data, which can increase report size and memory use. |
| Status-level Base64 | test.log(Status.FAIL, "details", MediaEntityBuilder.createScreenCaptureFromBase64String(base64).build()) |
A failure image kept with one log entry | No path can break, but large embedded images make copying and loading the report heavier. |
Choose a file when your CI system publishes an artifacts directory and you want to inspect images independently. Choose Base64 when portability is more important than keeping image files separate.
Rank #2
Using Base64 instead of a file
Base64 avoids copying a temporary file. Capture with OutputType.BASE64 and pass the returned string to ExtentReports:
String base64 = ((TakesScreenshot) driver)
.getScreenshotAs(OutputType.BASE64);
test.fail("Login failed",
MediaEntityBuilder
.createScreenCaptureFromBase64String(base64)
.build());
There is no external path to preserve in this form. The trade-off is that the encoded image becomes part of the report data. For suites producing many large screenshots, file references usually keep the HTML easier to transfer and open.
Capturing only after a failed test
Do not capture every passing step unless you need that diagnostic history. A failure hook can inspect the test result, capture the driver, attach the image, and let the normal suite cleanup flush once. In TestNG this is commonly placed in @AfterMethod; in JUnit it can be implemented as an extension. The exact hook API differs by framework, but the essential order remains:
- Determine that the test failed.
- Use the driver belonging to that test.
- Generate a unique destination path.
- Copy the screenshot and attach it to that test’s
ExtentTest. - Flush after all tests or at the lifecycle point your reporting design requires.
When a hook runs after the driver has already been quit, capture cannot work. Register cleanup so the screenshot hook executes before driver.quit().
Reliable paths in local and CI runs
- Keep the report and screenshot directory under one published artifact root, for example
target/Spark.htmlandtarget/screenshots/. - Prefer a relative path from the report location when your CI moves the whole directory together. If your reporting setup resolves paths differently, use the path convention required by that reporter and verify the generated HTML.
- Never delete or clean the screenshot directory before someone opens the report.
- Use names such as
CheckoutTest-chrome-thread-2.pngrather than a sharedfailure.pngin parallel execution. - Sanitize parameter values before placing them in filenames; slashes and operating-system separators can create unintended directories.
ExtentReports file attachments are links to saved images, not copies of those images. A report moved without its referenced files will show a broken image icon.
Common failures and fixes
Broken image or image icon
Cause: The image was deleted, the report was moved alone, or the recorded path is relative to a different directory than expected. Fix: open the generated HTML, inspect the image reference, and publish the referenced file at that exact relative location.
The failure appears but no screenshot is beside it
Cause: The media entity was attached to another test object or created without passing it to the status/log method. Fix: call test.fail(..., mediaEntity) or test.log(..., mediaEntity) on the same ExtentTest instance that owns the failure.
The HTML is empty or incomplete
Cause: flush() never ran, often because an exception bypassed normal cleanup. Fix: put extent.flush() in finally or the framework’s guaranteed teardown.
Rank #4
WebDriverException or UnsupportedOperationException
Cause: The selected driver does not provide screenshot support, or the session is no longer valid. Fix: verify that the driver implements TakesScreenshot, capture before quitting it, and check the driver’s own startup and session errors.
Parallel tests overwrite one another
Cause: Every thread writes the same filename. Fix: include the test identity and a thread or unique-run value in each destination path, and create directories safely before copying.
Screenshot is blank or shows the wrong state
Cause: Capture occurred before navigation or asynchronous rendering completed, or after the page had already changed. Fix: wait for the application condition your test requires, then capture at the failure boundary. A screenshot cannot recover pixels that were never rendered.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Performance and report-size decisions
PNG files preserve sharp text but can be larger than JPEG. Base64 adds encoding overhead and embeds bytes in the report; file references keep the HTML smaller but require artifact management. Capture only on failures, resize or compress images outside the reporting API when your retention policy allows it, and avoid flushing after every individual log in a large suite unless immediate persistence is required. A single final flush is usually simpler; a more frequent flush can reduce the amount of report data lost if the process terminates unexpectedly.
Best Value
Version compatibility
ExtentReports 4 and 5 share the core objects and media concepts. ExtentReports 5 examples use ExtentSparkReporter for HTML output. Check the major version in your Maven or Gradle build before copying imports or constructor signatures; mixing examples from different major versions is a common compile-time error.
Or skip the browser setup
If your requirement is a clean image of a URL rather than evidence from an already-running Selenium session, ScreenshotNeo can return a screenshot through one HTTP request. It removes cookie/consent banners, newsletter popups and chat widgets before capture; bot checks, blank pages, failed loads and cache hits are not billed; and its MCP server lets Claude, Cursor and other MCP clients call screenshot tools. The free plan includes 1,000 screenshots per month with no card, and paid plans start at $5 for 3,000 screenshots.
See the parameter reference in the ScreenshotNeo documentation. This returns an image for a URL; it does not replace a Selenium assertion screenshot when you need the exact authenticated browser session.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Create a free ScreenshotNeo account to use the 1,000 monthly screenshots without a card.
Practical implementation checklist
- Confirm the driver supports
TakesScreenshot. - Capture immediately after the failure is identified and before quitting the driver.
- Create the destination directory and copy the temporary file.
- Use unique names for parallel or parameterized tests.
- Attach the media to the same
ExtentTestfailure/log event. - Keep report and image paths together when publishing CI artifacts.
- Flush after all attachments, even when a test throws.
- Choose Base64 only when embedded portability outweighs report size and memory costs.
Frequently Asked Questions
Can I attach an element screenshot instead of the whole page?
Yes. Selenium screenshot support also applies to screenshot-capable HTML elements; obtain the element’s screenshot using the Selenium element API, then attach the resulting file or Base64 data with the same ExtentReports methods.
Should I call flush after every screenshot?
Not necessarily. A single flush in guaranteed teardown is usually sufficient; call it earlier only when your reporting lifecycle requires incremental persistence.
Why does my report work locally but fail in CI?
The CI job may publish the HTML without its referenced image directory, or it may resolve relative paths from a different working directory. Publish both together and inspect the generated image reference.
Quick Recap
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.

