Screenshots Only for Failed Tests (ITestListener)

To capture screenshots only when a test fails, use a TestNG Listener.

The commonly used interface is:

  • ITestListener

Override the:

  • onTestFailure()

method.

Whenever a test fails, TestNG automatically invokes this method.

Inside onTestFailure(), call your screenshot utility.

Simple Analogy

Think of it like a CCTV camera that records only when motion is detected, instead of recording continuously.


Steps Followed

Step 1: Create a Screenshot Utility

Use:

  • TakesScreenshot

to capture screenshots.


Step 2: Create a Listener Class

Implement:

ITestListener

Step 3: Override onTestFailure()

Capture the screenshot inside:

onTestFailure()

Step 4: Register the Listener

Register the listener using either:

  • testng.xml
  • @Listeners

Step 5: Store Screenshots

Save screenshots in a dedicated location.

Example:

/screenshots/FailedTests

The complete listener

public class ScreenshotListener implements ITestListener {

    @Override
    public void onTestFailure(ITestResult result) {
        WebDriver driver = DriverFactory.get();               // the ThreadLocal driver of this test's thread
        if (driver == null) return;                           // e.g. failure before the browser started

        File src = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
        String name = result.getTestClass().getRealClass().getSimpleName() + "_"
                + result.getMethod().getMethodName() + "_" + System.currentTimeMillis() + ".png";
        try {
            Path target = Paths.get("target", "screenshots", name);
            Files.createDirectories(target.getParent());
            Files.copy(src.toPath(), target);
            Reporter.log("Screenshot: " + target, true);     // also appears in TestNG's report
        } catch (IOException e) {
            Reporter.log("Could not save screenshot: " + e.getMessage(), true);
        }
    }
}

// Register it on a test class…
@Listeners(ScreenshotListener.class)
public class CheckoutTest extends BaseTest { … }
<!-- …or for the whole suite in testng.xml -->
<listeners>
  <listener class-name="com.shop.listeners.ScreenshotListener"/>
</listeners>

Timing matters: depending on the TestNG version, @AfterMethod can run before onTestFailure() — so if teardown calls driver.quit(), the listener finds a closed session and the screenshot fails with NoSuchSessionException. Two reliable fixes: take the screenshot inside @AfterMethod(alwaysRun = true) before quitting (check result.getStatus() == ITestResult.FAILURE), or move the quit() into the listener after the screenshot. Always get the driver from a ThreadLocal DriverFactory rather than a static field, so parallel tests capture their own browser; the timestamp in the file name stops retried or data-driven runs from overwriting each other.

@AfterMethod(alwaysRun = true)
public void tearDown(ITestResult result) {
    if (result.getStatus() == ITestResult.FAILURE) {
        ScreenshotUtil.capture(DriverFactory.get(), result.getMethod().getMethodName());   // browser still open
    }
    DriverFactory.quit();
}

Benefits

  • Screenshots only for failed tests
  • Cleaner reports
  • Reduced storage usage
  • Easy integration with Extent Reports
  • Easy integration with Allure Reports

STAR Answer

Situation

Screenshots were being generated for every test, making reports large and difficult to analyze.

Task

Capture screenshots only for failed test cases.

Action

Implemented an ITestListener and overrode onTestFailure() to call the screenshot utility.

Result

Reports became smaller, cleaner, and included screenshots only for failed test cases.


Advertisement

What Is ITestListener?

ITestListener is a TestNG interface that allows you to perform custom actions whenever test events occur.

It provides callback methods that execute automatically during the test lifecycle.


Common Callback Methods

  • onTestStart()
  • onTestSuccess()
  • onTestFailure()
  • onTestSkipped()
  • onFinish()

Registration Methods

The listener can be registered using:

  • testng.xml
  • @Listeners

Common Uses

  • Logging
  • Screenshot Capture
  • Report Enhancement
  • Custom Reporting

TestNG in the Framework: Grouping, Parallel Execution & Test Suites

Within the automation framework, TestNG acts as the execution control layer.


Grouping

Tests are organized into groups such as:

  • Smoke
  • Sanity
  • Regression

Specific groups can be executed whenever required.


Parallel Execution

TestNG supports parallel execution.

Parallel execution can be configured through:

  • testng.xml

Tests may execute in parallel at different levels, such as:

  • Methods
  • Classes
  • Tests

This significantly reduces execution time.

The same mechanism is also used for Cucumber parallel execution through a TestNG runner.


Test Suites

The:

testng.xml

file controls:

  • Test Suites
  • Test Classes
  • Test Methods
  • Include / Exclude Operations
  • Execution Order
  • Groups

Retry Logic

Retry execution is handled using:

  • RetryAnalyzer

This allows failed test cases to be re-executed automatically.


Reporting

TestNG integrates with:

  • Extent Reports
  • Allure Reports

It also generates the default:

  • Emailable Report

Framework Interview Line

"I used TestNG for execution control—grouping, parallel execution, and test suites using testng.xml. It was integrated with Maven and Jenkins as part of our CI/CD pipeline."


DataProvider & Execution Control

@DataProvider

@DataProvider is used for Data-Driven Testing.

It supplies multiple data sets to a single test method.

Each data row executes the same test once.


Priorities

Test execution order can be controlled using:

  • Priorities

testng.xml

The suite configuration file:

  • Controls execution
  • Controls grouping
  • Controls parallel execution
  • Supports execution analysis

More TestNG Interview Questions

How do you re-run only the failed tests?

After a run, TestNG writes testng-failed.xml (in test-output/, or target/surefire-reports/ with Maven). Run that file as a suite to execute only the failures: mvn test -Dsurefire.suiteXmlFiles=target/surefire-reports/testng-failed.xml.

What is IRetryAnalyzer?

An interface that decides whether a failed test should run again:

public class Retry implements IRetryAnalyzer {
    private int count = 0;
    private static final int MAX = 2;

    @Override
    public boolean retry(ITestResult result) {
        return count++ < MAX;   // retry up to 2 times
    }
}

@Test(retryAnalyzer = Retry.class)
public void checkout() { ... }

Use retries as a safety net for genuinely unstable environments, not to hide flaky tests — and log every retry.

How do you apply a retry analyzer to every test?

With an IAnnotationTransformer listener, which modifies @Test annotations at runtime:

public class RetryTransformer implements IAnnotationTransformer {
    @Override
    public void transform(ITestAnnotation annotation, Class testClass,
                          Constructor testConstructor, Method testMethod) {
        annotation.setRetryAnalyzer(Retry.class);
    }
}

Register it in testng.xml under <listeners>.

How do you run a specific group?

<test name="Smoke">
  <groups>
    <run>
      <include name="smoke"/>
      <exclude name="wip"/>
    </run>
  </groups>
  <classes>
    <class name="com.example.tests.LoginTest"/>
  </classes>
</test>

From Maven: mvn test -Dgroups=smoke. Keeping separate smoke.xml and regression.xml suites is also common.

How do you include or exclude specific methods?

<class name="com.example.tests.CartTest">
  <methods>
    <include name="addToCart"/>
    <exclude name="applyExpiredCoupon"/>
  </methods>
</class>

@BeforeMethod vs @BeforeClass?

@BeforeMethod runs before every test method (typical for starting a fresh browser); @BeforeClass runs once before the first test in the class (for shared, expensive setup).

How do you pass parameters from testng.xml?

// testng.xml:  <parameter name="browser" value="chrome"/>
@Parameters("browser")
@BeforeMethod
public void setUp(@Optional("chrome") String browser) { ... }

How does @DataProvider work?

It returns Object[][] (or an Iterator<Object[]>); the test runs once per row. Add parallel = true to run rows in parallel.

How do you run tests in parallel?

Set parallel="methods", "classes", "tests" or "instances" with a thread-count in the suite, and use a ThreadLocal<WebDriver> so threads never share a browser.

What does dependsOnMethods do?

The test runs only after the named methods pass; if they fail, it's skipped, not failed. Use it sparingly — independent tests are easier to run in parallel. alwaysRun = true runs it regardless.

How do you skip a test intentionally?

@Test(enabled = false) excludes it; throwing new SkipException("reason") skips it at runtime, for example when a feature flag is off.

SoftAssert vs hard Assert?

A hard Assert stops the test at the first failure. SoftAssert collects failures and reports them all when you call assertAll() — forget that call and the failures are silently ignored.

How do you run TestNG with Maven?

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <configuration>
    <suiteXmlFiles>
      <suiteXmlFile>testng.xml</suiteXmlFile>
    </suiteXmlFiles>
  </configuration>
</plugin>

What reports does TestNG generate, and how do you customise them?

TestNG produces index.html and emailable-report.html in test-output/, plus XML results. For richer reports, use a listener-based library such as Extent Reports or Allure, or implement IReporter for a fully custom report.

How do you integrate TestNG with Jenkins?

Run mvn clean test in the pipeline, publish the XML results (for example with junit 'target/surefire-reports/*.xml' or the TestNG Results plugin), and archive the HTML report and failure screenshots. See The Complete Jenkins Tutorial.

Why isn't TestNG detecting my test methods?

Check that the methods are public, that @Test is imported from org.testng.annotations (not JUnit), that the class is in testng.xml or matches Surefire's naming pattern, and that TestNG is on the test classpath.

How would you optimise a suite of 500+ tests?

Run in parallel on a Grid, split smoke and regression suites, create test data through APIs instead of the UI, move checks that don't need a browser to the API layer, remove duplicate UI coverage, and replace every fixed sleep with explicit waits.

Keep the syntax handy in the TestNG Cheat Sheet and see TestNG in a Selenium project in TestNG for Selenium.

Practise answering these under time pressure in the TestNG Interview Simulator.

From Real Projects

In interviews for testing and automation roles, I've found the questions come back to real work: the application you tested, how you wrote and reviewed test cases, how you tracked execution and defects (for me, TestRail and Jira), and how your automation was structured (Selenium, Java, TestNG, POM and a business library). Prepare one short, honest example from your own projects for each topic on this page. Testing Testsigma — a platform where users write automated tests in plain English — meant thinking like its users, who are testers themselves. Be ready to explain annotations, groups, data providers and parallel execution with examples.


📚 Official documentation: Selenium Grid documentation · Selenium documentation

FAQs

How Do You Capture Screenshots Only for Failed Test Cases?

Use ITestListener.

Override:

onTestFailure()

Call a screenshot utility built using:

TakesScreenshot

Register the listener using:

  • testng.xml
  • @Listeners

Store screenshots inside a dedicated failure folder.


What Is ITestListener?

ITestListener is a TestNG interface that provides callback methods for different test events.

Common methods include:

  • onTestStart()
  • onTestSuccess()
  • onTestFailure()
  • onTestSkipped()
  • onFinish()

It is commonly used for:

  • Logging
  • Screenshot Capture
  • Custom Reports

How Do You Run a Specific Group of Tests?

Assign tests to groups such as:

  • Smoke
  • Sanity
  • Regression

Run the required group using:

  • testng.xml
  • TestNG Run Configuration

How Do You Run Tests in Parallel?

Configure parallel execution inside:

testng.xml

Supported execution levels include:

  • Methods
  • Classes
  • Tests

Parallel execution is also commonly used for Cucumber through a TestNG runner.


What Is @DataProvider Used For?

@DataProvider supplies multiple datasets to a test method.

Each dataset executes the same test once, enabling Data-Driven Testing.