Direct answer: capture the browser with Selenium’s TakesScreenshot, copy the temporary OutputType.FILE result to a durable file, and attach that saved path to the matching ExtentTest. Use test.addScreenCaptureFromPath(path) for a test-level image, or pass MediaEntityBuilder.createScreenCaptureFromPath(path).build() to a log call for a failure-specific image. Capture before quitting the driver, and keep the image beside the generated report when you publish it.
The complete workflow
There are four separate operations. Selenium obtains the pixels, your code copies them out of Selenium’s temporary file, ExtentReports associates the durable file with a test or event, and your report archive preserves both the HTML and image asset. A missing copy step or a moved image directory is the usual reason a report shows a broken image.
- Call
((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE)while the driver still displays the relevant page. - Create a per-run media directory and choose a unique filename for the test or failure.
- Copy the temporary file to that filename, handling directory and I/O errors explicitly.
- Attach the durable path to the correct
ExtentTestobject. - Publish the report together with its referenced image files.
Capture and attach a file-based screenshot
The following Java utility uses the JDK file APIs, so the copy mechanism is not tied to a particular Apache Commons IO setup. It creates the directory, captures the image, copies it, and attaches it to a failure log.
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 java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.time.Instant;
public final class ExtentScreenshot {
private ExtentScreenshot() {
}
public static Path captureFailure(WebDriver driver,
ExtentTest test,
String testName,
String message) throws IOException {
Path mediaDirectory = Path.of("target", "extent-media");
Files.createDirectories(mediaDirectory);
String safeName = testName.replaceAll("[^a-zA-Z0-9._-]", "_");
Path destination = mediaDirectory.resolve(
safeName + "-" + Instant.now().toEpochMilli() + ".png");
File temporary = ((TakesScreenshot) driver)
.getScreenshotAs(OutputType.FILE);
Files.copy(temporary.toPath(), destination,
StandardCopyOption.REPLACE_EXISTING);
test.fail(message, MediaEntityBuilder
.createScreenCaptureFromPath(destination.toAbsolutePath().toString())
.build());
return destination;
}
}
OutputType.FILE returns a temporary file. Copy it before the JVM exits; do not assume that the temporary path remains available when a report is opened later. The utility uses a timestamp in the filename so parallel failures do not overwrite one another. A UUID is another suitable uniqueness strategy.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →#1 Best Overall
Calling the utility from a test
Your runner must already have a live WebDriver and the corresponding ExtentTest. The capture belongs in the failure path before driver.quit().
ExtentTest test = extent.createTest("Checkout validation");
WebDriver driver = new ChromeDriver();
try {
driver.get("https://example.test/checkout");
// Perform assertions and interactions here.
test.pass("Checkout page loaded");
} catch (Throwable failure) {
try {
ExtentScreenshot.captureFailure(
driver, test, "Checkout-validation",
failure.getMessage() == null ? "Test failed" : failure.getMessage());
} catch (IOException captureError) {
test.warning("The test failed, but screenshot capture failed: "
+ captureError.getMessage());
}
throw failure;
} finally {
driver.quit();
}
The example deliberately treats screenshot failure as a reporting problem rather than hiding the original test failure. If your policy is to continue after a capture error, log that error with enough context to find the driver, destination directory, and test name.
Choose test-level or log-level attachment
Test-level image
Use addScreenCaptureFromPath when the image represents the overall outcome or when you want a simple attachment on the test node.
test.addScreenCaptureFromPath(savedPath.toString());
This is useful when several steps contribute to one result and the image does not need to appear beside a particular log message.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Log-level image
Use the media builder when the screenshot explains one event, assertion, or exception. The image appears with that log entry.
test.fail("Submit button produced an error",
MediaEntityBuilder
.createScreenCaptureFromPath(savedPath.toString())
.build());
Attach the image to the same ExtentTest instance that recorded the event. In listener-based designs, keep the test object associated with the current thread or scenario so a failure cannot receive another test’s screenshot.
Keep report paths valid after the run
File-based ExtentReports reporters reference an image path; they do not automatically place the image bytes inside the HTML. Store media in a directory that travels with the report, such as target/extent-media, and archive that directory with the generated HTML.
- Prefer a path layout that is stable from the report’s location. If your reporter resolves relative paths, calculate the path relative to the report output directory rather than the process working directory.
- Do not rename or clean the media directory after report generation unless you update every reference.
- When copying reports to a server, copy the entire report bundle, not only the HTML file.
- For parallel execution, include a run identifier, class or scenario name, and a unique suffix in each filename.
- Sanitize names derived from test data so slashes, colons, and other path characters cannot create unintended directories.
Use Base64 when a separate image file is undesirable
Selenium can return a screenshot as Base64, and ExtentReports exposes matching Base64 methods. This removes the path-management step in the association call, but the encoded image increases report content and may affect how your reporter, web server, or archive handles large reports.
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 minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallString base64 = ((TakesScreenshot) driver)
.getScreenshotAs(OutputType.BASE64);
test.addScreenCaptureFromBase64String(base64);
// Or attach it to a particular event:
test.fail("Login assertion failed",
MediaEntityBuilder
.createScreenCaptureFromBase64String(base64)
.build());
Choose Base64 when the report is intentionally self-contained and its size is acceptable. Choose a file path when you want ordinary image assets that can be inspected, compressed, cached, and archived independently. Neither choice changes when the capture must happen: the driver and page state must still exist.
Capture at the right lifecycle point
A screenshot taken after the browser is closed cannot show the failed page. Place capture code in the failure hook before teardown. The exact hook depends on your runner:
Rank #3
- JUnit: put the capture in the failure branch of an extension or teardown callback while the driver is still available.
- TestNG: use the failure callback or listener that can retrieve both the current driver and its
ExtentTest. - Cucumber: capture from the scenario failure hook and attach it to that scenario’s Extent test.
- Custom runners: make the driver, test object, and output directory part of the same execution context.
These frameworks expose different callback signatures, so verify the method names against the versions in your build. The ExtentReports 4 and 5 documentation shows related APIs; do not mix examples across major versions without checking your actual dependency.
Common errors and fixes
ClassCastException when casting the driver
Cause: the active driver implementation does not expose Selenium’s screenshot interface in the way your code expects, or the object being cast is not the WebDriver instance.
Fix: capture from the live browser driver and verify that it implements TakesScreenshot. Do not cast a wrapper, a null reference, or a driver that has already been quit.
The report shows a broken image
Cause: the temporary file was used directly, the image was not copied, or the report was moved without its media directory.
Fix: copy to a durable destination, attach that destination, and archive the HTML and image directory together. Check whether your reporter expects an absolute path or a path relative to the report.
The screenshot is from the wrong page
Cause: capture occurred after navigation, cleanup, or a later test changed the browser state.
Fix: take the screenshot immediately when the assertion or command fails, before recovery navigation and before teardown.
Two tests display the same image
Cause: concurrent tests reused one filename.
Fix: include a unique run, test, and timestamp or UUID in every destination name. Avoid sharing one mutable driver between parallel tests.
The screenshot itself fails
Cause: the browser is gone, the session has crashed, the destination directory is unwritable, or the copy operation failed.
Fix: report capture errors separately, preserve the original assertion, create directories before capture, and verify write permissions in the CI workspace. A failed screenshot should not replace the diagnostic that caused the test to fail.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Base64 makes the report unwieldy
Cause: every image is embedded in the report content instead of stored as an external asset.
Fix: switch to file paths for large suites or many screenshots, and retain only the images needed for failure diagnosis.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
For a screenshot of a URL outside your Selenium run, ScreenshotNeo provides a single HTTP request. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
Use the API documentation at https://screenshotneo.com/docs/ for authentication and options. The following calls use the supplied endpoint and return a WebP file.
Recommended Free Tools
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(`HTTP ${res.status}`);
require('fs').writeFileSync('shot.webp', Buffer.from(await res.arrayBuffer()));
ScreenshotNeo supports full-page captures with lazy images loaded, CSS-selector element captures, dark mode, 12 device presets or custom viewports, retina scale, PDF output, custom CSS and JavaScript, clicks before capture, selector or delay waits, network-idle waits, ad and tracker blocking, custom headers and cookies, user agents, Authorization, timezone and geolocation, transparent backgrounds, resizing, configurable-TTL caching, signed image links, asynchronous jobs with signed webhooks, up to 100 URLs per bulk call, a usage API, an OpenAPI specification, and compatibility with parameter names used by other screenshot APIs.
Every feature is included on every plan: Free provides 1,000 screenshots per month with no card; Starter is $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. Sign up for the free ScreenshotNeo plan to get 1,000 screenshots a month with no card.
Practical checklist
- Capture while the failing browser state is visible.
- Use
OutputType.FILEand copy it to a durable, unique destination. - Attach with
addScreenCaptureFromPathfor a test image orMediaEntityBuilderfor an event image. - Keep the media directory with the generated report.
- Use Base64 only when the resulting report size and serving model are acceptable.
- Verify the APIs against the ExtentReports major version actually installed.
- Handle capture and copy errors without masking the original test failure.
Frequently Asked Questions
Can I attach more than one screenshot to an ExtentTest?
Yes. Call the appropriate path or Base64 attachment method for each image on the same test object; use distinct filenames for file-based images.
Should I use an absolute or relative screenshot path?
Use the form your configured reporter resolves reliably, and test the report from the same directory structure used by CI or your archive.
Does Selenium embed an OutputType.FILE image in the report automatically?
No. The temporary Selenium file must be copied and then referenced by ExtentReports, or you can use the Base64 APIs.
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.

