</>

Technology

Page Object Model

Difficulty

Advanced

Interview Question

How do you implement parallel test execution with ThreadLocal WebDriver in a Selenium framework?

Answer

Parallel Test Execution with ThreadLocal WebDriver

Why ThreadLocal?

Without ThreadLocal, a shared static WebDriver causes thread interference:

CODE
Thread 1: opens Chrome → clicks Login
Thread 2: opens Chrome → OVERWRITES driver reference
Thread 1: tries to click → NullPointerException or wrong browser!

With ThreadLocal, each thread gets its own WebDriver:

CODE
Thread 1: driver = ChromeDriver #1 → clicks Login
Thread 2: driver = ChromeDriver #2 → independent
No interference!

DriverManager with ThreadLocal

Java
// driver/DriverManager.java
public class DriverManager {

    // ThreadLocal stores separate driver per thread
    private static ThreadLocal<WebDriver> driverThread = new ThreadLocal<>();

    public static WebDriver getDriver() {
        return driverThread.get();
    }

    public static void initDriver(String browser, boolean headless) {
        WebDriver driver;

        switch (browser.toLowerCase()) {
            case "firefox":
                FirefoxOptions ffOpts = new FirefoxOptions();
                if (headless) ffOpts.addArguments("--headless");
                driver = new FirefoxDriver(ffOpts);
                break;

            case "edge":
                driver = new EdgeDriver();
                break;

            default: // chrome
                ChromeOptions opts = new ChromeOptions();
                if (headless) opts.addArguments(
                    "--headless=new", "--no-sandbox",
                    "--disable-dev-shm-usage", "--window-size=1920,1080");
                driver = new ChromeDriver(opts);
        }

        driver.manage().window().maximize();
        driver.manage().timeouts()
              .implicitlyWait(Duration.ofSeconds(10));

        driverThread.set(driver);   // set for THIS thread only
    }

    public static void quitDriver() {
        if (driverThread.get() != null) {
            driverThread.get().quit();
            driverThread.remove();   // IMPORTANT: prevent memory leak
        }
    }
}

BaseTest — Parallel-Safe Setup

Java
// base/BaseTest.java
public class BaseTest {

    @BeforeMethod(alwaysRun = true)
    public void setUp(Method method) {
        String browser  = ConfigReader.getBrowser();
        boolean headless = ConfigReader.isHeadless();

        DriverManager.initDriver(browser, headless);
        DriverManager.getDriver().get(ConfigReader.getBaseUrl());
    }

    @AfterMethod(alwaysRun = true)
    public void tearDown(ITestResult result) {
        if (result.getStatus() == ITestResult.FAILURE) {
            ScreenshotUtil.capture(result.getName());
        }
        DriverManager.quitDriver();   // quits THIS thread's driver
    }
}

testng.xml — Enable Parallel Execution

XML
<!-- Run each test METHOD in parallel (max 3 at a time) -->
<suite name="Parallel Suite" parallel="methods" thread-count="3">
    <test name="Regression">
        <classes>
            <class name="com.automateqa.tests.LoginTest"/>
            <class name="com.automateqa.tests.SearchTest"/>
            <class name="com.automateqa.tests.CheckoutTest"/>
        </classes>
    </test>
</suite>

<!-- OR: run each <test> block in parallel (different env/browser) -->
<suite name="Cross-Browser Suite" parallel="tests" thread-count="3">
    <test name="Chrome Tests">
        <parameter name="browser" value="chrome"/>
        <classes><class name="com.automateqa.tests.SmokeTest"/></classes>
    </test>
    <test name="Firefox Tests">
        <parameter name="browser" value="firefox"/>
        <classes><class name="com.automateqa.tests.SmokeTest"/></classes>
    </test>
    <test name="Edge Tests">
        <parameter name="browser" value="edge"/>
        <classes><class name="com.automateqa.tests.SmokeTest"/></classes>
    </test>
</suite>

Thread-Safe Extent Reports Logging

Java
// Use ThreadLocal for ExtentTest too!
public class TestListener implements ITestListener {
    private static ExtentReports                extent = ExtentManager.getInstance();
    private static ThreadLocal<ExtentTest>       test   = new ThreadLocal<>();

    public static ExtentTest getTest() { return test.get(); }

    @Override
    public void onTestStart(ITestResult result) {
        test.set(extent.createTest(result.getMethod().getMethodName()));
    }

    @Override
    public void onTestSuccess(ITestResult result) { test.get().pass("PASSED"); }

    @Override
    public void onTestFailure(ITestResult result) {
        test.get().fail(result.getThrowable());
        test.get().addScreenCaptureFromPath(ScreenshotUtil.capture(result.getName()));
    }

    @Override
    public void onFinish(ITestContext context) { extent.flush(); }
}

Follow AutomateQA

Related Topics