Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC 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 & 11Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Use a TestNG ITestListener and capture the browser in onTestFailure(ITestResult), before teardown closes the WebDriver. Save the image under a path that your build publishes, then add a relative link or attachment using the reporting library your project actually uses. TestNG detects the failure; Selenium captures the image; the report integration connects the two.
What the solution must do
A reliable failure screenshot has four separate responsibilities:
- Identify the exact WebDriver instance that ran the failed invocation.
- Capture it from
onTestFailurewhile the session is still alive. - Write a uniquely named file to a report-accessible directory.
- Attach that file, or emit a relative link, through your report tool.
TestNG’s listener callback does not automatically know where your driver is stored, and TestNG’s default HTML output does not provide one universal image-attachment API. Those are project-specific decisions.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Prerequisites and project decisions
- Java, Selenium WebDriver, and TestNG dependencies that match the versions used by your build.
- A driver lifecycle that lets the listener retrieve the driver for the failed test.
- A published artifact directory, such as
target/surefire-reportsorbuild/reports/tests. - A report library (if you need embedded images rather than links).
Do not hide the driver in an unrelated global singleton when tests run in parallel. Use a base test accessor, dependency injection, or a ThreadLocal<WebDriver> whose value is bound to the current invocation.
#1 Best Overall
Implement the failure listener
Listener code
The following class is intentionally explicit about the parts that vary by project. Replace driverFor, screenshotPathFor, and attachToReport with your own implementations.
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.testng.ITestListener;
import org.testng.ITestResult;
public final class FailureScreenshotListener implements ITestListener {
@Override
public void onTestFailure(ITestResult result) {
WebDriver driver = driverFor(result); // project-specific lookup
if (driver == null) {
System.err.println("No WebDriver available for " + result.getName());
return;
}
try {
File temporary = ((TakesScreenshot) driver)
.getScreenshotAs(OutputType.FILE);
Path destination = screenshotPathFor(result);
Files.createDirectories(destination.getParent());
Files.copy(temporary.toPath(), destination,
StandardCopyOption.REPLACE_EXISTING);
attachToReport(result, destination);
} catch (WebDriverException | IOException captureError) {
// Keep the assertion failure as the primary result.
System.err.println("Could not capture failure screenshot: "
+ captureError.getMessage());
}
}
private WebDriver driverFor(ITestResult result) {
// Return the driver owned by this test invocation.
throw new UnsupportedOperationException("Implement driver lookup");
}
private Path screenshotPathFor(ITestResult result) {
String className = result.getTestClass().getName();
String method = result.getMethod().getMethodName();
String invocation = Integer.toString(result.getMethod().getCurrentInvocationCount());
String safe = (className + "-" + method + "-" + invocation)
.replaceAll("[^A-Za-z0-9._-]", "_");
return Path.of("target", "surefire-reports", "screenshots", safe + ".png");
}
private void attachToReport(ITestResult result, Path image) {
// Call the attachment API of your report library, or log a relative link.
System.out.println("Failure screenshot: " + image);
}
}
Selenium’s TakesScreenshot interface can return several representations. OutputType.FILE is convenient for copying; use OutputType.BYTES or OutputType.BASE64 when your reporter accepts in-memory data. A driver or element implementation may throw WebDriverException, and an implementation that does not support screenshots may throw UnsupportedOperationException.
Use a thread-local driver safely
public final class DriverStore {
private static final ThreadLocal<WebDriver> CURRENT = new ThreadLocal<>();
public static void set(WebDriver driver) { CURRENT.set(driver); }
public static WebDriver get() { return CURRENT.get(); }
public static void clear() { CURRENT.remove(); }
}
Create the driver and call DriverStore.set(driver) in setup, retrieve DriverStore.get() from the listener, and clear it only after the listener has had a chance to capture. If your framework stores drivers on a base class, have driverFor resolve the test instance from result.getInstance() instead.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsRank #2
Make the file visible in the report
Relative link in a plain HTML report
When the report is static HTML, place images beneath the report directory and write a relative anchor such as <a href="screenshots/LoginTest-signIn-0.png">Screenshot</a>. The build must publish both the HTML file and the screenshots directory; publishing only the HTML produces broken links.
Third-party reporters
Extent-style, Allure-style, and other reporters expose different attachment calls and lifecycle objects. Invoke that library’s method from attachToReport, passing either the copied path, bytes, or Base64 string it documents. Do not assume that TestNG’s Reporter.log embeds an image; it can record text or a link, while image rendering remains a reporter concern.
TestNG’s generated output
TestNG writes its generated reports to the configured output directory and exposes an index.html entry point plus XML output. Treat those files as destinations for your build artifacts, not as proof of a built-in, universal screenshot attachment feature.
Register the listener
Suite-wide XML registration
<suite name="UI suite">
<listeners>
<listener class-name="com.example.FailureScreenshotListener"/>
</listeners>
<test name="Chrome tests">
<classes>
<class name="com.example.LoginTest"/>
</classes>
</test>
</suite>
Annotation registration
import org.testng.annotations.Listeners;
@Listeners(FailureScreenshotListener.class)
public class LoginTest {
// tests
}
The annotation applies at suite scope for the tests it covers, so use it deliberately. XML is preferable when you want an environment-specific listener without changing test classes.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Driver lifetime, retries, and parallel execution
Capture before teardown
If an @AfterMethod or suite teardown calls quit() before the failure callback, the session is gone and no screenshot can be taken. Arrange teardown ordering so the listener runs while the driver remains available. A defensive listener should still catch capture errors and preserve the original assertion.
Generate collision-proof names
Method names alone collide across classes, data-provider rows, retries, and workers. Include class, method, invocation number, and a safe unique suffix. Sanitize slashes, spaces, and other path characters before writing.
Rank #4
Separate concurrent sessions
TestNG’s parallel modes do not define your driver association. Your storage design does. Keep one driver per invocation or thread, never mutate a shared driver, and ensure two workers cannot overwrite the same destination.
Decide what retries mean
A retry analyzer can execute the same method several times. Decide whether each failed attempt gets an image or only the final failure; include the attempt in the filename if you retain all of them. onTestFailure is not a catch-all for timeouts, skips, or failures allowed by a success-percentage rule. Add handling for onTestFailedButWithinSuccessPercentage, onTestSkipped, or onTestFailedWithTimeout only when those outcomes matter to your report.
Common failures and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| No image is created | The listener was never registered, or the driver lookup returned null. | Verify XML/annotation registration and log the test instance, thread, and driver identity. |
NoSuchSessionException or a closed-session error |
Teardown quit the browser first. | Move capture earlier in the lifecycle; do not clear the driver store before the callback. |
ClassCastException |
The driver does not implement TakesScreenshot. |
Use a screenshot-capable WebDriver implementation or record that capture is unsupported. |
| Files exist locally but links are broken in CI | The artifact collector published HTML but not the image directory, or the relative path is wrong. | Publish the complete report tree and test the link from the CI artifact root. |
| Images overwrite one another | Filename contains only the method name. | Add class, invocation/retry identity, and a unique suffix. |
| Only some failures have screenshots | Those outcomes use timeout/skip callbacks, or the reporter flushes before attachment. | Handle the relevant callbacks and flush the reporter after attachment. |
| Screenshot shows only a viewport | WebDriver screenshot semantics are implementation-dependent for page, frame, window, or display scope. | Do not promise full-page capture unless your browser/driver combination explicitly supports the method you use. |
When Selenide is a better fit
Projects already using Selenide may not need this listener for ordinary Selenide checks: its documentation says screenshots are taken automatically when those checks fail, normally under build/reports/tests. You can change the directory with Configuration.reportsFolder. Selenide also documents a TestNG ScreenShooter listener for broader success and failure behavior, including non-Selenide assertions. Confirm the behavior against the Selenide version and report integration in your build before treating it as a drop-in replacement.
| Approach | Setup ownership | Attachment behavior | Best fit |
|---|---|---|---|
| Custom Selenium listener | You own driver lookup, paths, lifecycle, and reporter calls. | Whatever your report library supports. | Plain Selenium projects and custom pipelines. |
| Selenide automation | Selenide manages common failure capture; you configure folders/listeners. | Integrated with its documented reporting conventions. | Teams already using Selenide checks. |
Or skip the browser setup
ScreenshotNeo provides a website screenshot API and MCP server. One request returns PNG, JPEG, WebP, or PDF; it accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP tools let Claude, Cursor, or another MCP client call take_screenshot, get_page_info, and capture_pdf.
For a direct capture, see the ScreenshotNeo API documentation:
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}`);
Every plan includes the same feature set: full-page and selector capture, device and retina controls, dark mode, custom CSS/JavaScript, waits, request blocking, headers/cookies, timezone and geolocation, resizing, chosen-TTL caching, signed links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage reporting, and an OpenAPI specification. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to start.
Recommended Free Tools
Operational checklist
- Register the listener in the intended scope.
- Prove that the failed invocation resolves to the correct driver.
- Capture before
quit()and preserve the original throwable. - Create directories and use collision-resistant filenames.
- Attach with the actual reporter API or a relative link.
- Publish images alongside HTML/XML reports in CI.
- Test retries, data providers, parallel workers, skips, and timeouts separately.
Frequently Asked Questions
Can I capture a screenshot from an ITestListener after the test method returns?
Yes, if the WebDriver session is still open. If teardown has already called quit(), the listener cannot recover that browser state.
Does onTestFailure capture skipped tests or timeouts?
Not necessarily. TestNG exposes separate callbacks for skips, timeouts, and failures within a success-percentage allowance; implement those callbacks when your reporting policy requires images.
Is a Selenium screenshot always a full-page image?
No. The result depends on the conformant WebDriver implementation and the method used; it may represent a page, window, frame, or display.
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.

