Failure Screenshots in Selenium TestNG
Failure Screenshots are screenshots captured automatically when a Selenium automation test fails. They provide visual evidence of the browser state at the time of failure and help testers understand what was displayed on the screen when an assertion, element interaction, navigation, synchronization, or other test step failed.
In Selenium TestNG frameworks, failure screenshots are commonly implemented using Selenium's TakesScreenshot interface together with TestNG's ITestListener. The listener can capture the browser state inside onTestFailure() before the WebDriver session is closed.
Failure screenshots are especially useful in CI/CD environments because the tester may not have direct access to the browser that executed the test. A screenshot saved as a test artifact can provide immediate visual evidence of the failed state.
Course Resource: Selenium Training | Register for Course Demo
1. What are Failure Screenshots?
A failure screenshot is an image captured from the browser when an automated test fails. It records the visible browser state at or near the time the failure is detected.
For example, suppose a Selenium test expects a successful login but the application displays an error message. The test may fail because the expected dashboard is not displayed. A failure screenshot can show the actual page, error message, popup, or other visible state.
Instead of investigating the failure only from logs, the QA engineer can inspect the screenshot and understand what the browser displayed.
2. Why are Failure Screenshots Important?
Failure screenshots provide visual evidence that complements test logs, stack traces, assertions, and reports.
- Helps identify the visual state of a failed test.
- Makes debugging easier.
- Provides evidence for defect investigation.
- Helps identify unexpected error messages.
- Helps identify missing or incorrectly displayed elements.
- Useful when tests run on remote machines.
- Useful in CI/CD pipelines.
- Improves test reports.
- Reduces debugging time.
- Helps identify intermittent failures.
- Provides historical evidence of failures.
- Can be attached to automation reports.
3. Failure Screenshot Flow
The basic failure screenshot workflow is:
TestNG starts test
|
v
Selenium executes test steps
|
v
Assertion / WebDriver operation fails
|
v
TestNG marks test as failed
|
v
ITestListener.onTestFailure()
|
v
Get WebDriver instance
|
v
TakesScreenshot
|
v
Capture screenshot
|
v
Save PNG file
|
v
Attach / publish screenshot
|
v
Test Report
4. Selenium TakesScreenshot Interface
Selenium provides the TakesScreenshot interface for screenshot capture. A WebDriver implementation that supports screenshot capture can be cast to this interface and used with getScreenshotAs().
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
TakesScreenshot screenshot = (TakesScreenshot) driver;
File source = screenshot.getScreenshotAs(OutputType.FILE);
The captured result can be returned as a file, byte array, or Base64 representation depending on the selected Selenium output type.
5. Basic Screenshot Capture
The simplest approach is to capture a screenshot manually inside a test or utility method.
File source = ((TakesScreenshot) driver)
.getScreenshotAs(OutputType.FILE);
The returned file should then be copied to a permanent destination if it needs to be retained after the test execution.
6. Saving a Screenshot to a Directory
A screenshot should normally be stored in a dedicated directory such as screenshots, test-output/screenshots, or test-artifacts/screenshots.
File source = ((TakesScreenshot) driver)
.getScreenshotAs(OutputType.FILE);
File destination = new File(
"screenshots/failure.png"
);
FileUtils.copyFile(source, destination);
In production frameworks, use a unique filename so that one failed test does not overwrite the screenshot produced by another test.
7. Using Apache Commons IO
Apache Commons IO is commonly used in Java Selenium projects to copy the temporary screenshot file to a permanent location.
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
File source = ((TakesScreenshot) driver)
.getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(
source,
new File("screenshots/failure.png")
);
8. Screenshot with Timestamp
A timestamp can be included in the filename to prevent screenshots from being overwritten.
String timestamp = new SimpleDateFormat(
"yyyyMMdd_HHmmss"
).format(new Date());
File source = ((TakesScreenshot) driver)
.getScreenshotAs(OutputType.FILE);
File destination = new File(
"screenshots/failure_" + timestamp + ".png"
);
FileUtils.copyFile(source, destination);
A timestamp is especially useful when the same test is executed repeatedly.
9. Screenshot with Test Method Name
Including the test method name makes it easier to identify which test generated the screenshot.
String methodName = result.getName();
String timestamp = new SimpleDateFormat(
"yyyyMMdd_HHmmss"
).format(new Date());
String fileName = methodName
+ "_" + timestamp
+ ".png";
A typical filename might look like:
loginTest_20261001_132100.png
10. What is ITestListener?
ITestListener is a TestNG listener interface that provides callback methods for different test execution events.
One of its most useful methods for screenshot automation is onTestFailure().
public class FailureScreenshotListener
implements ITestListener {
@Override
public void onTestFailure(ITestResult result) {
System.out.println("Test failed");
}
}
The listener can be used to automatically execute screenshot logic whenever a test fails.
11. onTestFailure() Method
The onTestFailure() callback is invoked when TestNG reports a failed test result. It receives an ITestResult object containing information about the failed test.
@Override
public void onTestFailure(ITestResult result) {
System.out.println(
"Failed Test: " + result.getName()
);
}
The result object can be used to obtain the test method name, test class, parameters, and other execution information.
12. Basic Failure Screenshot Listener
import java.io.File;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.testng.ITestListener;
import org.testng.ITestResult;
public class FailureScreenshotListener
implements ITestListener {
private WebDriver driver;
@Override
public void onTestFailure(ITestResult result) {
System.out.println(
"Test Failed: " + result.getName()
);
if (driver instanceof TakesScreenshot) {
File source =
((TakesScreenshot) driver)
.getScreenshotAs(OutputType.FILE);
try {
FileUtils.copyFile(
source,
new File(
"screenshots/"
+ result.getName()
+ ".png"
)
);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
In a real framework, the listener should obtain the correct WebDriver instance associated with the failed test rather than maintaining an unsafe shared driver field.
13. Why Capture Before driver.quit()?
The screenshot should be captured while the WebDriver session is still active. If the browser has already been closed using driver.quit(), the listener may no longer be able to capture the browser state.
Test fails
|
v
onTestFailure()
|
v
Capture Screenshot
|
v
Save Screenshot
|
v
@AfterMethod
|
v
driver.quit()
This ordering is important for reliable failure evidence.
14. Screenshot and @AfterMethod
TestNG frameworks commonly use @AfterMethod for browser cleanup.
@AfterMethod(alwaysRun = true)
public void tearDown() {
if (driver != null) {
driver.quit();
}
}
The failure listener should capture the screenshot while the browser is still available, and teardown should perform cleanup afterward.
15. Manual Screenshot vs Automatic Screenshot
| Manual Screenshot | Automatic Failure Screenshot |
| Developer explicitly calls screenshot method | Listener captures screenshot automatically |
| Useful at selected checkpoints | Useful for failed tests |
| Requires code inside test flow | Centralized implementation |
| Can capture successful states | Usually captures failure states |
| More repetitive | More reusable |
16. Registering Listener with @Listeners
TestNG allows listeners to be registered using the @Listeners annotation.
import org.testng.annotations.Listeners;
@Listeners(FailureScreenshotListener.class)
public class LoginTest {
@Test
public void loginTest() {
// Test steps
}
}
The listener can also be placed on a shared base class when the project architecture is designed to apply the listener to the relevant test classes.
17. Registering Listener in testng.xml
A listener can also be registered in the TestNG XML suite configuration.
<suite name="Automation Suite">
<listeners>
<listener
class-name="listeners.FailureScreenshotListener"/>
</listeners>
<test name="UI Tests">
<classes>
<class name="tests.LoginTest"/>
</classes>
</test>
</suite>
XML registration is useful when the same listener should apply across many test classes.
18. Screenshot Utility Class
Instead of writing screenshot code repeatedly, create a reusable utility class.
public class ScreenshotUtil {
public static void capture(
WebDriver driver,
String fileName) {
try {
File source =
((TakesScreenshot) driver)
.getScreenshotAs(OutputType.FILE);
File destination =
new File(
"screenshots/" + fileName
);
FileUtils.copyFile(
source,
destination
);
} catch (Exception e) {
e.printStackTrace();
}
}
}
19. Using Screenshot Utility in a Test
@Test
public void loginTest() {
driver.get(
"https://example.com/login"
);
try {
driver.findElement(
By.id("username")
).sendKeys("admin");
driver.findElement(
By.id("password")
).sendKeys("wrong");
driver.findElement(
By.id("loginButton")
).click();
} catch (Exception e) {
ScreenshotUtil.capture(
driver,
"login_failure.png"
);
throw e;
}
}
Although this approach works, a listener is usually more convenient for framework-wide automatic failure screenshots.
20. Capturing Screenshot with ITestResult
The ITestResult object can provide the failed test's method name.
@Override
public void onTestFailure(ITestResult result) {
String testName =
result.getMethod()
.getMethodName();
System.out.println(
"Failed Test: " + testName
);
}
This value can be incorporated into the screenshot filename.
21. Unique Screenshot Naming
Screenshot filenames should be unique, especially when tests run in parallel.
String className =
result.getTestClass()
.getName();
String methodName =
result.getMethod()
.getMethodName();
String fileName =
className.replace(".", "_")
+ "_"
+ methodName
+ "_"
+ System.currentTimeMillis()
+ ".png";
A robust filename can contain the class name, method name, timestamp, thread ID, and retry information when required.
22. Failure Screenshot with Thread ID
Parallel test execution can cause multiple tests to fail at approximately the same time. A thread ID can help create unique filenames.
long threadId =
Thread.currentThread().getId();
String fileName =
result.getName()
+ "_thread_"
+ threadId
+ "_"
+ System.currentTimeMillis()
+ ".png";
23. Screenshot Directory Structure
A clean directory structure makes screenshots easier to locate.
test-artifacts
|
|-- screenshots
| |-- LoginTest
| | |-- loginTest_001.png
| | |-- loginTest_002.png
| |
| |-- SearchTest
| |-- searchTest_001.png
| |-- searchTest_002.png
|
|-- reports
| |-- testng-results.xml
| |-- index.html
24. Screenshot and Test Reports
A screenshot file and a test report are separate artifacts. Capturing a PNG does not automatically mean that every report format will embed that image.
A reporting framework can provide an attachment mechanism, or a custom HTML report can create a relative link to the screenshot.
Test Result
|
+-- PASS
|
+-- FAIL
|
+-- Error Message
+-- Stack Trace
+-- Screenshot
+-- Test Data
25. Adding Screenshot Link to HTML Report
A simple HTML report can link to a screenshot using a relative path.
<a href="screenshots/loginTest_failure.png"
target="_blank">
View Failure Screenshot
</a>
The screenshot and report should be published together so that the relative path remains valid.
26. Screenshot as Base64
Selenium can also return a screenshot as Base64 data.
String screenshot =
((TakesScreenshot) driver)
.getScreenshotAs(
OutputType.BASE64
);
Base64 can be useful when a reporting library accepts encoded image data directly.
27. Screenshot as Bytes
Another option is to obtain the screenshot as bytes.
byte[] screenshot =
((TakesScreenshot) driver)
.getScreenshotAs(
OutputType.BYTES
);
Byte arrays can be useful for APIs or reporting systems that accept image data without requiring a temporary file.
28. Screenshot Output Types
| Output Type | Purpose |
| OutputType.FILE | Returns screenshot as a file |
| OutputType.BYTES | Returns screenshot as byte data |
| OutputType.BASE64 | Returns screenshot as Base64 encoded data |
29. Full Failure Screenshot Listener Example
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.testng.ITestListener;
import org.testng.ITestResult;
public class FailureScreenshotListener
implements ITestListener {
@Override
public void onTestFailure(
ITestResult result) {
Object testInstance =
result.getInstance();
if (!(testInstance
instanceof BaseTest)) {
return;
}
WebDriver driver =
((BaseTest) testInstance)
.getDriver();
if (!(driver instanceof TakesScreenshot)) {
return;
}
try {
String timestamp =
new SimpleDateFormat(
"yyyyMMdd_HHmmss"
).format(new Date());
String methodName =
result.getMethod()
.getMethodName();
File source =
((TakesScreenshot) driver)
.getScreenshotAs(
OutputType.FILE
);
File destination =
new File(
"test-artifacts/screenshots/"
+ methodName
+ "_"
+ timestamp
+ ".png"
);
FileUtils.copyFile(
source,
destination
);
System.out.println(
"Screenshot saved: "
+ destination.getAbsolutePath()
);
} catch (Exception e) {
System.err.println(
"Screenshot capture failed: "
+ e.getMessage()
);
}
}
}
30. Base Test Class for Driver Access
A base test class can expose the WebDriver instance to the listener.
public class BaseTest {
protected WebDriver driver;
public WebDriver getDriver() {
return driver;
}
@BeforeMethod
public void setUp() {
driver = new ChromeDriver();
driver.manage()
.window()
.maximize();
}
@AfterMethod(alwaysRun = true)
public void tearDown() {
if (driver != null) {
driver.quit();
driver = null;
}
}
}
31. Failure Screenshot with Base Test
@Listeners(FailureScreenshotListener.class)
public class LoginTest extends BaseTest {
@Test
public void loginTest() {
driver.get(
"https://example.com/login"
);
driver.findElement(
By.id("username")
).sendKeys("admin");
driver.findElement(
By.id("password")
).sendKeys("wrong");
driver.findElement(
By.id("loginButton")
).click();
Assert.assertTrue(
driver.getTitle()
.contains("Dashboard")
);
}
}
If the assertion fails, the listener can capture the browser state before the driver is closed by teardown.
32. Failure Screenshots for Assertions
Failure screenshots are particularly useful when assertions fail.
String actualTitle =
driver.getTitle();
Assert.assertEquals(
actualTitle,
"Dashboard"
);
If the actual title is different, the screenshot can show the page that was actually displayed at the time of failure.
33. Failure Screenshots for Element Not Found
A screenshot can also help investigate NoSuchElementException failures.
driver.findElement(
By.id("loginButton")
).click();
If the element is missing, the screenshot may show an unexpected page, loading state, popup, error message, or changed UI.
34. Failure Screenshots for Timeout Failures
Synchronization failures can also benefit from screenshots.
WebDriverWait wait =
new WebDriverWait(
driver,
Duration.ofSeconds(10)
);
wait.until(
ExpectedConditions
.visibilityOfElementLocated(
By.id("dashboard")
)
);
If the expected element does not appear, a screenshot can provide additional information about the browser state.
35. Failure Screenshots for Wrong URL
Assert.assertEquals(
driver.getCurrentUrl(),
"https://example.com/dashboard"
);
A failure screenshot can help determine whether the application displayed an error page, login page, redirect page, or another unexpected screen.
36. Failure Screenshots for Wrong Page Title
Assert.assertEquals(
driver.getTitle(),
"Dashboard"
);
The screenshot provides visual evidence in addition to the assertion failure message.
37. Failure Screenshots for Login Testing
Login automation is one of the most common use cases.
@DataProvider(name = "loginData")
public Object[][] loginData() {
return new Object[][] {
{"admin", "admin123"},
{"manager", "manager123"},
{"invalid", "wrong123"}
};
}
@Test(dataProvider = "loginData")
public void loginTest(
String username,
String password) {
driver.findElement(
By.id("username")
).sendKeys(username);
driver.findElement(
By.id("password")
).sendKeys(password);
driver.findElement(
By.id("loginButton")
).click();
Assert.assertTrue(
driver.getTitle()
.contains("Dashboard")
);
}
When a particular data set fails, the framework should make it possible to identify the corresponding screenshot without exposing sensitive credentials.
38. Failure Screenshots with Data Providers
Data Providers create multiple test invocations from different data sets. When screenshots are captured, filenames should distinguish different invocations.
loginTest_admin_001.png
loginTest_manager_002.png
loginTest_invalid_003.png
Do not include passwords, tokens, session identifiers, or other secrets in filenames.
39. Failure Screenshots in Parallel Execution
Parallel execution requires special attention because multiple WebDriver sessions may be active at the same time.
Thread 1
|
+-- Chrome
+-- LoginTest
+-- failure_001.png
Thread 2
|
+-- Firefox
+-- SearchTest
+-- failure_002.png
Thread 3
|
+-- Edge
+-- CheckoutTest
+-- failure_003.png
Each test invocation should have access to its own WebDriver instance, and screenshot filenames should be unique.
40. ThreadLocal WebDriver
A common framework approach for parallel Selenium execution is to maintain a WebDriver instance per thread.
public class DriverManager {
private static ThreadLocal<WebDriver>
driver = new ThreadLocal<>();
public static void setDriver(
WebDriver webDriver) {
driver.set(webDriver);
}
public static WebDriver getDriver() {
return driver.get();
}
public static void quitDriver() {
if (driver.get() != null) {
driver.get().quit();
driver.remove();
}
}
}
This architecture helps the screenshot listener obtain the driver associated with the correct execution context.
41. Screenshot Naming in Parallel Tests
A parallel-safe screenshot filename can include class, method, thread, timestamp, and a unique identifier.
String fileName =
result.getTestClass()
.getName()
+ "_"
+ result.getMethod()
.getMethodName()
+ "_thread_"
+ Thread.currentThread().getId()
+ "_"
+ System.currentTimeMillis()
+ ".png";
42. Screenshot and Retry Analyzer
Some TestNG frameworks retry failed tests. When retries are used, decide whether each failed attempt should produce a separate screenshot.
Attempt 1
|
+-- FAIL
+-- screenshot_attempt_1.png
|
v
Retry
|
Attempt 2
|
+-- FAIL
+-- screenshot_attempt_2.png
Keeping separate screenshots can be useful when investigating flaky tests because each attempt may show a different browser state.
43. Failure Screenshots and Flaky Tests
A flaky test may pass during one execution and fail during another. Failure screenshots can help compare the visual state of different failed executions.
- Compare page loading state.
- Check unexpected popups.
- Check missing elements.
- Check stale or incomplete page content.
- Check unexpected redirects.
- Check browser-level error messages.
44. Screenshot and Browser Alerts
Unexpected JavaScript alerts can interfere with screenshot commands and WebDriver interactions. The framework should handle such situations carefully and preserve the original failure information.
try {
driver.findElement(
By.id("submit")
).click();
} catch (Exception e) {
ScreenshotUtil.capture(
driver,
"alert_failure.png"
);
throw e;
}
45. Screenshot and Multiple Windows
If a test opens multiple browser windows or tabs, the screenshot represents the current WebDriver context. The framework should make sure the correct window is selected before attempting the capture.
String mainWindow =
driver.getWindowHandle();
for (String handle :
driver.getWindowHandles()) {
driver.switchTo()
.window(handle);
}
46. Screenshot and Frames
When working with iframes, the current WebDriver context matters. A screenshot may reflect the current browser state, while an element-level screenshot can target a specific element.
driver.switchTo()
.frame("paymentFrame");
driver.findElement(
By.id("cardNumber")
).sendKeys("1234");
47. Full Page vs Viewport Screenshot
A standard WebDriver screenshot should not automatically be treated as a guaranteed full-page capture. The captured area can depend on the browser, driver, implementation, and capture method.
For ordinary failure debugging, the visible browser state is often useful. If complete-page evidence is required, use a browser or framework capability that explicitly supports the desired full-page behavior.
48. Element Screenshot
Selenium can also capture a screenshot of an individual element when supported by the WebElement screenshot functionality.
WebElement logo =
driver.findElement(
By.id("logo")
);
File source =
logo.getScreenshotAs(
OutputType.FILE
);
Element screenshots are useful when the failure is specifically related to one component of the page.
49. Screenshot on Success vs Failure
| Failure Screenshot | Success Screenshot |
| Captured when test fails | Captured when test passes or at selected checkpoints |
| Primarily for debugging | Primarily for evidence or documentation |
| Reduces unnecessary files | Can produce many artifacts |
| Useful in CI/CD | Useful for business evidence |
50. Screenshot Storage Strategy
A framework should define where screenshots are stored and how long they are retained.
Project
|
|-- test-artifacts
| |-- screenshots
| |-- logs
| |-- reports
|
|-- src
| |-- test
| |-- java
| |-- resources
For CI systems, the screenshot directory should be configured as a build artifact when the project requires screenshots to remain accessible after the workspace is cleaned.
51. Screenshot and Jenkins
Jenkins can publish test results and retain build artifacts. Screenshot files should be stored in a predictable directory and configured as artifacts when they need to be available after the build.
Jenkins Job
|
v
Checkout Code
|
v
Maven Build
|
v
TestNG Tests
|
v
Failure
|
v
Screenshot
|
v
test-artifacts/screenshots
|
v
Publish Artifacts
|
v
Jenkins Build Results
Publishing the test result and publishing screenshot files are separate concerns; both should be configured when visual failure evidence is required.
52. Screenshot and Maven
A Maven-based Selenium TestNG project can store screenshots under the build output directory.
mvn clean test
A common project structure is:
target
|
|-- test-output
| |-- screenshots
| |-- reports
|
|-- surefire-reports
53. Screenshot and CI/CD Pipeline
Developer Commit
|
v
CI Pipeline
|
v
Build
|
v
Execute Tests
|
+---- PASS
|
+---- FAIL
|
v
Capture Screenshot
|
v
Save Test Artifact
|
v
Generate Report
|
v
Publish Results
54. Failure Screenshots and Logs
Screenshots should complement logs rather than replace them.
| Evidence | Provides |
| Screenshot | Visual browser state |
| Log | Execution information |
| Stack Trace | Technical failure location |
| Test Report | Overall execution result |
| Test Data | Input used for execution |
55. Avoid Exposing Sensitive Information
Screenshots can contain sensitive information such as usernames, personal information, tokens, account details, or business data. Test frameworks should consider the sensitivity of captured browser content.
- Do not intentionally capture passwords.
- Do not include credentials in screenshot filenames.
- Avoid logging authentication tokens.
- Control access to screenshot artifacts.
- Apply retention policies to test artifacts.
- Mask sensitive information where practical.
56. Common Mistakes in Failure Screenshots
- Calling driver.quit() before screenshot capture.
- Using a shared WebDriver in parallel tests.
- Using the same filename for every failure.
- Not creating the screenshot directory.
- Ignoring screenshot exceptions completely.
- Overwriting screenshots from retry attempts.
- Storing screenshots outside the published CI artifact directory.
- Putting passwords or tokens in filenames.
- Assuming every screenshot is automatically embedded in every report.
- Capturing screenshots without identifying the corresponding test.
- Using a static driver for concurrent test execution.
57. Best Practices for Failure Screenshots
- Use ITestListener for centralized failure handling.
- Capture the screenshot before the WebDriver session is closed.
- Use unique filenames.
- Include test class and method information.
- Include thread information for parallel execution.
- Keep screenshots in a dedicated artifact directory.
- Publish screenshots with CI build artifacts when required.
- Keep screenshot failures secondary to the original test failure.
- Use thread-safe WebDriver management.
- Avoid sensitive information in filenames and logs.
- Use external reporting tools when richer attachments are required.
- Keep screenshot utilities reusable.
- Clean old artifacts according to project retention requirements.
58. Screenshot Capture Should Not Hide Original Failure
The screenshot operation is diagnostic support. If screenshot capture itself fails, it should not replace the original assertion or WebDriver failure.
try {
captureScreenshot(driver);
} catch (Exception screenshotError) {
System.err.println(
"Screenshot failed: "
+ screenshotError.getMessage()
);
}
// Preserve the original test failure
This approach helps keep the original failure as the primary diagnostic information.
59. Handling Screenshot Capture Exceptions
try {
File source =
((TakesScreenshot) driver)
.getScreenshotAs(
OutputType.FILE
);
FileUtils.copyFile(
source,
destination
);
} catch (Exception e) {
System.err.println(
"Unable to capture screenshot: "
+ e.getMessage()
);
}
Capture errors can occur when the browser session has already ended, the driver does not support the requested operation, the destination cannot be written, or another runtime problem occurs.
60. Practical Screenshot Utility
public class ScreenshotUtil {
public static String capture(
WebDriver driver,
String testName) {
String timestamp =
new SimpleDateFormat(
"yyyyMMdd_HHmmss"
).format(new Date());
String fileName =
testName
+ "_"
+ timestamp
+ ".png";
File destination =
new File(
"test-artifacts/screenshots/"
+ fileName
);
try {
File source =
((TakesScreenshot) driver)
.getScreenshotAs(
OutputType.FILE
);
FileUtils.copyFile(
source,
destination
);
return destination
.getAbsolutePath();
} catch (Exception e) {
System.err.println(
"Screenshot failed: "
+ e.getMessage()
);
return null;
}
}
}
61. Complete Listener Architecture
TestNG
|
v
Test Execution
|
v
Test Failure
|
v
ITestListener
|
v
onTestFailure()
|
v
Identify Test Instance
|
v
Get WebDriver
|
v
TakesScreenshot
|
v
Capture Image
|
v
Unique File Name
|
v
Save Test Artifact
|
+--------+--------+
| |
v v
HTML Report CI Artifact
| |
+--------+--------+
|
v
Test Evidence
62. Practical Project Structure
src
|-- test
|-- java
|-- tests
| |-- LoginTest.java
| |-- SearchTest.java
| |-- CheckoutTest.java
|
|-- listeners
| |-- FailureScreenshotListener.java
|
|-- utilities
| |-- ScreenshotUtil.java
| |-- DriverManager.java
|
|-- base
|-- BaseTest.java
test-artifacts
|-- screenshots
|-- reports
|-- logs
63. Complete Practical Failure Screenshot Example
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.ITestListener;
import org.testng.ITestResult;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;
@Listeners(FailureScreenshotListener.class)
public class LoginTest {
private WebDriver driver;
@BeforeMethod
public void setUp() {
driver = new ChromeDriver();
driver.manage()
.window()
.maximize();
driver.get(
"https://example.com/login"
);
}
@Test
public void loginTest() {
driver.findElement(
By.id("username")
).sendKeys("admin");
driver.findElement(
By.id("password")
).sendKeys("admin123");
driver.findElement(
By.id("loginButton")
).click();
Assert.assertTrue(
driver.getTitle()
.contains("Dashboard")
);
}
public WebDriver getDriver() {
return driver;
}
@AfterMethod(alwaysRun = true)
public void tearDown() {
if (driver != null) {
driver.quit();
driver = null;
}
}
}
64. Complete FailureScreenshotListener Example
import java.io.File;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.testng.ITestListener;
import org.testng.ITestResult;
public class FailureScreenshotListener
implements ITestListener {
@Override
public void onTestFailure(
ITestResult result) {
try {
Object instance =
result.getInstance();
if (!(instance
instanceof LoginTest)) {
return;
}
LoginTest test =
(LoginTest) instance;
WebDriver driver =
test.getDriver();
if (!(driver instanceof TakesScreenshot)) {
return;
}
File source =
((TakesScreenshot) driver)
.getScreenshotAs(
OutputType.FILE
);
String testName =
result.getMethod()
.getMethodName();
File destination =
new File(
"test-artifacts/screenshots/"
+ testName
+ "_"
+ System.currentTimeMillis()
+ ".png"
);
FileUtils.copyFile(
source,
destination
);
System.out.println(
"Failure screenshot saved: "
+ destination
.getAbsolutePath()
);
} catch (Exception e) {
System.err.println(
"Could not capture failure screenshot: "
+ e.getMessage()
);
}
}
}
65. Failure Screenshot with Data Provider
Failure screenshots become even more useful when a Data Provider executes the same test with multiple data sets.
@DataProvider(name = "products")
public Object[][] products() {
return new Object[][] {
{"Laptop"},
{"Mobile"},
{"Tablet"},
{"Headphones"}
};
}
@Test(dataProvider = "products")
public void searchTest(String product) {
driver.findElement(
By.id("search")
).sendKeys(product);
driver.findElement(
By.id("searchButton")
).click();
Assert.assertTrue(
driver.getTitle()
.contains(product)
);
}
A robust framework can include a sanitized product identifier in the screenshot filename so the failed data set is easier to identify.
66. Failure Screenshots with Page Object Model
Failure screenshots can be integrated with the Page Object Model. The page classes handle browser interaction while the listener handles failure evidence.
Test Class
|
v
Page Object
|
v
Selenium WebDriver
|
v
Application
|
v
Failure
|
v
TestNG Listener
|
v
Screenshot Utility
This separation keeps screenshot management independent from page-specific Selenium interactions.
67. Failure Screenshots and Reporting Tools
Reporting tools can provide richer ways to display screenshots alongside test results. The exact attachment mechanism depends on the reporting library and project configuration.
A common architecture is:
TestNG
|
+-- Test Result
|
+-- Failure Details
|
+-- Screenshot
|
+-- Logs
|
v
Automation Report
When using a third-party reporting library, follow that library's supported screenshot attachment API rather than assuming that saving a PNG automatically embeds it into the report.
68. Failure Screenshots in Regression Testing
Regression suites can contain hundreds or thousands of automated tests. Automatically capturing screenshots only when tests fail can provide useful debugging evidence without generating screenshots for every successful execution.
For regression automation, combine screenshots with:
- Test name.
- Failure message.
- Stack trace.
- Browser name.
- Environment.
- Test data identifier.
- Timestamp.
- Execution thread.
69. Failure Screenshots in Cross-Browser Testing
When the same test executes on Chrome, Firefox, and Edge, the screenshot filename should identify the browser.
LoginTest_Chrome_failure.png
LoginTest_Firefox_failure.png
LoginTest_Edge_failure.png
This makes browser-specific failures easier to investigate.
70. Cross-Browser Failure Architecture
TestNG
|
+-----------+-----------+
| | |
Chrome Firefox Edge
| | |
v v v
Test A Test A Test A
| | |
v v v
Failure Pass Failure
| |
v v
Screenshot Screenshot
| |
+-----------+-----------+
|
v
Report
71. Failure Screenshot and Environment
Testing across QA, staging, and production-like environments can create similar failures with different visual states. The environment name can be included in the screenshot path.
screenshots
|
|-- QA
| |-- LoginTest_failure.png
|
|-- Stage
| |-- LoginTest_failure.png
|
|-- Production
|-- LoginTest_failure.png
72. Failure Screenshot and Browser Information
For large automation suites, it can be useful to record browser information alongside screenshot metadata.
Browser: Chrome
Environment: QA
Test: LoginTest
Method: validLogin
Thread: 4
Timestamp: 20261001_132100
Screenshot: LoginTest_validLogin_4.png
This information can be stored in logs or reports without putting unnecessary sensitive information into the image filename.
73. Failure Screenshots for E-Commerce Testing
E-commerce applications contain many workflows where failure screenshots are valuable.
- Login.
- Product search.
- Product details.
- Add to cart.
- Cart validation.
- Checkout.
- Address selection.
- Payment page.
- Order confirmation.
- Order history.
Search
|
v
Product
|
v
Cart
|
v
Checkout
|
v
Payment
|
v
Confirmation
|
+---- Failure Screenshot
74. Failure Screenshots for Form Testing
Forms are another common use case because visual evidence can show validation messages and incorrectly displayed fields.
Registration Form
|
+-- Name
+-- Email
+-- Mobile
+-- Password
+-- Confirm Password
|
v
Submit
|
v
Validation
|
+-- PASS
|
+-- FAIL
|
v
Failure Screenshot
75. Failure Screenshots for UI Validation
UI automation often verifies visibility, text, buttons, labels, menus, images, and page layouts. A screenshot can provide useful visual context when such checks fail.
Assert element visible
|
v
Element missing
|
v
Test Failure
|
v
Screenshot
|
v
Visual Investigation
76. Failure Screenshots for Navigation Testing
Home
|
v
Login
|
v
Dashboard
|
v
Profile
|
v
Settings
|
v
Unexpected Page
|
v
Failure Screenshot
The screenshot can help determine which page was displayed when navigation validation failed.
77. Failure Screenshot Retention
Screenshot files can accumulate rapidly in large test suites. Define a retention strategy for local and CI environments.
- Keep recent failed-test screenshots.
- Archive screenshots for important release builds.
- Remove obsolete artifacts.
- Use CI artifact retention settings.
- Keep production-sensitive artifacts access-controlled.
78. Performance Considerations
Screenshot capture adds file-generation and storage work when a failure occurs. In normal failure-only designs, successful tests do not generate screenshot files.
For very large suites, avoid unnecessarily capturing multiple copies of the same failure and keep the listener lightweight.
79. Troubleshooting Failure Screenshots
| Problem | Possible Cause | Solution |
| No screenshot | Listener not registered | Check @Listeners or testng.xml |
| No screenshot | Driver is null | Initialize driver before test |
| No screenshot | Driver already closed | Capture before quit() |
| Files overwritten | Duplicate filename | Use timestamp/UUID/thread ID |
| Screenshot not in CI | Artifact not published | Publish screenshot directory |
| Wrong browser screenshot | Shared driver | Use isolated driver instances |
| Blank/incomplete image | Browser state or capture limitation | Check browser state and capture timing |
| Original failure hidden | Screenshot exception replaces failure | Handle screenshot exception separately |
80. Common Failure Screenshot Architecture Mistakes
- Using a static WebDriver for parallel execution.
- Capturing the screenshot after browser teardown.
- Not registering the listener.
- Using an incorrect driver reference.
- Saving all screenshots with the same name.
- Not creating the destination directory.
- Not copying temporary screenshot files to durable storage.
- Not publishing screenshots in CI.
- Including credentials in screenshot names.
- Allowing screenshot errors to hide the original test failure.
81. Best Practices Checklist
- Use a reusable Screenshot Utility.
- Use TestNG ITestListener for automatic failure capture.
- Capture screenshots before WebDriver teardown.
- Use unique and meaningful filenames.
- Include class and method names.
- Include browser and environment information where useful.
- Use thread-safe driver management for parallel tests.
- Store screenshots in a predictable directory.
- Publish screenshots as CI artifacts.
- Integrate screenshots with reports where supported.
- Do not expose sensitive information.
- Preserve the original assertion or WebDriver exception.
- Clean up old artifacts according to project requirements.
82. Interview Questions on Failure Screenshots
1. What is a failure screenshot?
A failure screenshot is an image captured from the browser when an automated test fails.
2. Which Selenium interface is used for screenshots?
The TakesScreenshot interface is commonly used for browser screenshot capture.
3. Which Selenium method captures a screenshot?
The getScreenshotAs() method is used to obtain screenshot output.
4. Which TestNG listener method is commonly used for failure screenshots?
onTestFailure(ITestResult result) is commonly used.
5. Why should screenshots be captured before driver.quit()?
Because the browser session needs to remain available for the screenshot operation.
6. How can a listener be registered in TestNG?
It can be registered using @Listeners or through testng.xml.
7. Why should screenshot filenames be unique?
Unique names prevent one failure from overwriting another screenshot.
8. How can timestamps be added to screenshots?
A Java date/time formatter or another unique identifier can be included in the filename.
9. Can screenshots be used with Data Providers?
Yes. Screenshot filenames can identify the test invocation while avoiding sensitive test data.
10. Can screenshots be used with Page Object Model?
Yes. Screenshot handling can remain in a listener or utility while page objects manage application interactions.
11. Can screenshots be captured during parallel execution?
Yes, but each test invocation should use an appropriate isolated WebDriver and unique screenshot destination.
12. What is OutputType.FILE?
It requests the screenshot as a file representation.
13. What is OutputType.BYTES?
It returns the screenshot as byte data, which can be useful for report attachment APIs.
14. What is OutputType.BASE64?
It returns the screenshot as Base64 encoded data.
15. Does saving a PNG automatically attach it to every TestNG report?
No. Saving the image and attaching or linking it in a particular report are separate operations.
16. Why should screenshot exceptions not replace the original failure?
The original test failure contains the primary diagnostic information and should remain visible even if screenshot capture fails.
17. Can screenshots be published in Jenkins?
Yes. The screenshot directory can be retained and published as a build artifact.
18. What information should not be included in screenshot filenames?
Passwords, tokens, session identifiers, and other sensitive information should not be included.
19. Why are screenshots useful in CI/CD?
They provide visual evidence when the browser executes on a remote build agent where the tester cannot directly observe the browser.
20. What is the biggest advantage of automatic failure screenshots?
They provide reusable visual evidence automatically whenever a test fails, reducing the need for manual reproduction.
83. Quick Reference Table
| Concept | Description |
| TakesScreenshot | Selenium interface for screenshot capture |
| getScreenshotAs() | Captures screenshot output |
| OutputType.FILE | Returns screenshot as a file |
| OutputType.BYTES | Returns screenshot bytes |
| OutputType.BASE64 | Returns Base64 screenshot data |
| ITestListener | TestNG listener interface |
| onTestFailure() | Callback used for failed-test handling |
| @Listeners | Registers a TestNG listener |
| testng.xml | Can register listeners at suite level |
| Screenshot Utility | Reusable screenshot capture component |
| ThreadLocal WebDriver | Common approach for thread-specific drivers |
| CI Artifact | Persistent build output such as screenshots |
84. Learning Roadmap for Failure Screenshots
- Understand Selenium WebDriver.
- Learn Selenium screenshot concepts.
- Learn the TakesScreenshot interface.
- Understand getScreenshotAs().
- Learn OutputType.FILE.
- Learn OutputType.BYTES and BASE64.
- Create a Screenshot Utility.
- Learn TestNG listeners.
- Implement ITestListener.
- Implement onTestFailure().
- Register listeners using @Listeners.
- Register listeners using testng.xml.
- Integrate screenshots with Page Object Model.
- Integrate screenshots with Data Providers.
- Handle parallel execution safely.
- Publish screenshots in CI/CD.
- Integrate screenshots with test reports.
- Apply security and retention practices.
85. Practical Exercises
- Create a Selenium Screenshot Utility.
- Capture a screenshot manually after a button click.
- Create a TestNG ITestListener.
- Capture a screenshot inside onTestFailure().
- Register the listener using @Listeners.
- Register the listener using testng.xml.
- Add timestamps to screenshot filenames.
- Add test method names to screenshot filenames.
- Integrate screenshots with a LoginTest.
- Integrate screenshots with a Data Provider.
- Integrate screenshots with Page Object Model.
- Execute tests in parallel and create unique screenshot names.
- Publish screenshots as Maven/CI artifacts.
- Create an HTML report with screenshot links.
- Implement a complete failure evidence framework.
86. Real-World Failure Investigation Example
Suppose a checkout test fails because the expected confirmation message is not displayed.
Checkout Test
|
v
Add Product
|
v
Open Cart
|
v
Checkout
|
v
Place Order
|
v
Assert Confirmation
|
X
Test Failed
|
v
ITestListener
|
v
Failure Screenshot
|
+-- Browser State
+-- Error Message
+-- Test Name
+-- Timestamp
|
v
Report / CI Artifact
The QA engineer can inspect the screenshot together with the stack trace and logs to understand what happened during the failed execution.
87. Recommended Framework Architecture
Selenium Test Framework
|
+-------------------+-------------------+
| | |
Tests Page Objects Utilities
| | |
| | +--------+--------+
| | | |
| | Driver Screenshot
| | Manager Utility
| | | |
+-------------------+----------+-----------------+
|
v
TestNG
|
v
ITestListener
|
v
Failure Screenshot
|
+---------+---------+
| |
v v
Report CI Artifact
88. Summary
Failure screenshots are an important part of a professional Selenium automation framework because they provide visual evidence of the browser state when an automated test fails.
Selenium provides the TakesScreenshot interface and getScreenshotAs() method for screenshot capture. TestNG's ITestListener and onTestFailure() provide a convenient mechanism for automatically triggering screenshot capture when tests fail.
A robust implementation should capture the screenshot while the WebDriver session is still active, save the image to a durable and predictable location, use unique filenames, and publish the screenshot with the test results when required.
For advanced Selenium frameworks, failure screenshots can be combined with Page Object Model, Data Providers, parallel execution, reusable Driver Managers, Maven, CI/CD, Jenkins, logs, and reporting systems.
89. Final Takeaway
Failure Screenshots = Test Failure + Visual Evidence.
A good Selenium framework should not only tell the team that a test failed; it should also make it easier to understand what the browser looked like at the time of failure. By combining Selenium screenshot capabilities with TestNG listeners, reusable utilities, proper WebDriver management, unique filenames, and CI artifact publishing, failure investigation becomes more structured and efficient.
90. Course Resources
Learn more about Selenium automation, WebDriver, TestNG, Page Object Model, and related automation testing concepts:
Quick Revision: Use TakesScreenshot to capture screenshots, use ITestListener.onTestFailure() for automatic failure handling, capture before driver.quit(), use unique filenames, keep WebDriver thread-safe for parallel execution, preserve the original failure, and publish screenshots as test artifacts when running automation in CI/CD.