Handling Exceptions in Selenium with TestNG

Why Handle Exceptions in TestNG?

When writing Selenium test scripts with TestNG, handling exceptions properly ensures that:
βœ”οΈ Tests don't fail unexpectedly.
βœ”οΈ Proper error messages are logged.
βœ”οΈ Test execution continues without stopping other test cases.
βœ”οΈ Retry mechanisms can be implemented for failed test cases.


Methods to Handle Exceptions in TestNG

1. Using Try-Catch in Test Methods

The simplest way to handle exceptions in TestNG is by using try-catch blocks within test methods.

Example: Handling NoSuchElementException in a TestNG Test

import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

public class ExceptionHandlingTest {
    WebDriver driver;

    @BeforeMethod
    public void setup() {
        System.setProperty("webdriver.chrome.driver", "path/to/chromedriver");
        driver = new ChromeDriver();
        driver.get("https://example.com");
    }

    @Test
    public void testHandleException() {
        try {
            WebElement element = driver.findElement(By.id("nonexistent")); // Element may not be present
            element.click();
        } catch (NoSuchElementException e) {
            System.out.println("Handled NoSuchElementException: " + e.getMessage());
        }
    }

    @AfterMethod
    public void teardown() {
        driver.quit();
    }
}

βœ… Pros: Simple, prevents test failure.
❌ Cons: Can hide real test failures if used excessively.


2. Using Expected Exceptions in TestNG (expectedExceptions)

If you expect a specific exception in a test case, you can use TestNG's expectedExceptions attribute to handle it automatically.

Example: Handling NoSuchElementException with expectedExceptions

import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

public class ExpectedExceptionTest {
    WebDriver driver;

    @BeforeMethod
    public void setup() {
        System.setProperty("webdriver.chrome.driver", "path/to/chromedriver");
        driver = new ChromeDriver();
        driver.get("https://example.com");
    }

    @Test(expectedExceptions = NoSuchElementException.class)
    public void testExpectedException() {
        WebElement element = driver.findElement(By.id("nonexistent")); // This will throw NoSuchElementException
        element.click();
    }

    @AfterMethod
    public void teardown() {
        driver.quit();
    }
}

βœ… Pros: Clean and avoids unnecessary try-catch blocks.
❌ Cons: Can't log detailed error messages.


3. Using TestNG's @Listeners for Global Exception Handling

Instead of handling exceptions in every test case, TestNG allows you to create a listener class that logs or reports exceptions globally.

Step 1: Create a Listener Class for Exception Handling

import org.testng.ITestListener;
import org.testng.ITestResult;

public class TestListener implements ITestListener {
    @Override
    public void onTestFailure(ITestResult result) {
        System.out.println("Test Failed: " + result.getName());
        System.out.println("Reason: " + result.getThrowable().getMessage());
    }

    @Override
    public void onTestSuccess(ITestResult result) {
        System.out.println("Test Passed: " + result.getName());
    }
}

Step 2: Apply Listener to the TestNG Test Class

import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.*;

@Listeners(TestListener.class)
public class ListenerTest {
    WebDriver driver;

    @BeforeMethod
    public void setup() {
        System.setProperty("webdriver.chrome.driver", "path/to/chromedriver");
        driver = new ChromeDriver();
        driver.get("https://example.com");
    }

    @Test
    public void testWithListener() {
        WebElement element = driver.findElement(By.id("nonexistent")); // This will trigger onTestFailure
        element.click();
    }

    @AfterMethod
    public void teardown() {
        driver.quit();
    }
}

βœ… Pros: Centralized exception handling, useful for reporting.
❌ Cons: Can't handle exceptions at runtime, only logs failures.


4. Using @Test and dependsOnMethods to Skip on Failure

If a test method depends on another method and the first one fails, TestNG will automatically skip the dependent tests.

Example: Skipping a Test if Login Fails

import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.Assert;
import org.testng.annotations.*;

public class DependencyTest {
    WebDriver driver;

    @BeforeMethod
    public void setup() {
        System.setProperty("webdriver.chrome.driver", "path/to/chromedriver");
        driver = new ChromeDriver();
        driver.get("https://example.com");
    }

    @Test
    public void loginTest() {
        try {
            WebElement loginButton = driver.findElement(By.id("login")); // If not found, test fails
            loginButton.click();
        } catch (NoSuchElementException e) {
            Assert.fail("Login button not found!");
        }
    }

    @Test(dependsOnMethods = "loginTest")
    public void dashboardTest() {
        System.out.println("Dashboard Test Executed");
    }

    @AfterMethod
    public void teardown() {
        driver.quit();
    }
}

βœ… Pros: Automatically skips tests if dependencies fail.
❌ Cons: If dependency is wrongly defined, unrelated tests may be skipped.


5. Using Retry Logic for Failed Tests

TestNG provides a way to automatically retry failed test cases using the IRetryAnalyzer interface.

Step 1: Create a Retry Class

import org.testng.IRetryAnalyzer;
import org.testng.ITestResult;

public class RetryFailedTests implements IRetryAnalyzer {
    private int retryCount = 0;
    private static final int maxRetryCount = 2; // Retry twice

    @Override
    public boolean retry(ITestResult result) {
        if (retryCount < maxRetryCount) {
            retryCount++;
            return true;
        }
        return false;
    }
}

Step 2: Apply Retry Logic to a Test

import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.*;

public class RetryTest {
    WebDriver driver;

    @BeforeMethod
    public void setup() {
        System.setProperty("webdriver.chrome.driver", "path/to/chromedriver");
        driver = new ChromeDriver();
        driver.get("https://example.com");
    }

    @Test(retryAnalyzer = RetryFailedTests.class)
    public void flakyTest() {
        WebElement element = driver.findElement(By.id("randomElement")); // Sometimes fails
        element.click();
    }

    @AfterMethod
    public void teardown() {
        driver.quit();
    }
}

βœ… Pros: Retries flaky tests, reducing false failures.
❌ Cons: Can mask real issues if overused.


Conclusion

TestNG provides multiple ways to handle exceptions in Selenium:
βœ”οΈ try-catch for local exception handling.
βœ”οΈ expectedExceptions for predefined exception handling.
βœ”οΈ @Listeners for centralized failure logging.
βœ”οΈ dependsOnMethods to skip tests if a dependency fails.
βœ”οΈ IRetryAnalyzer to retry failed tests automatically.