Latest post from this blog

How to Manage Test Execution Across Different Browsers and Environments (QA, UAT, Staging)

In real-time automation projects, test execution is never limited to a single browser or a single environment . Applications must be validated across multiple browsers (Chrome, Firefox, Edge) and multiple environments such as QA, UAT, and Staging before going live. A well-designed Selenium + Java + Cucumber automation framework should allow testers to switch browsers and environments easily without changing test scripts . This blog explains how to manage test execution efficiently across different browsers and environments using best practices followed in real projects. Why Multi-Browser and Multi-Environment Testing Is Important Different users use different browsers QA, UAT, and Staging environments have different configurations Bugs may appear only in specific environments or browsers Same test cases must be validated everywhere before production release Common Challenges Testers Face Hardcoded browser names and URLs Maintaining separate test scripts for each environment Browse...

Ensuring Thread Safety in Parallel Test Execution with Selenium, Cucumber, and Java

🔒 What Is Thread Safety in Selenium Automation?

When multiple threads (tests or scenarios) run simultaneously, they must not interfere with each other’s data or browser sessions.

⚠️ Example: Common Problem Without Thread Safety

If your WebDriver is declared as:

public static WebDriver driver;

and you run tests in parallel, all tests share the same driver object — leading to several issues such as:

1️⃣ Browser windows closing unexpectedly

2️⃣ Wrong data being accessed by other tests

3️⃣ Screenshots captured from the wrong browser

4️⃣ Flaky or inconsistent test results

5️⃣ One test closing the browser while another is still running

6️⃣ Tests opening URLs in the same browser window

7️⃣ Random “Session not found” errors

➡️ Without thread safety, your parallel runs become unstable and unreliable.

💡 How To Achieve Thread Safety

We can achieve thread safety using the ThreadLocal class in our automation framework.

🧰 What Is ThreadLocal?

ThreadLocal provides each thread with its own copy of a variable.

So, if you store your WebDriver in a ThreadLocal, each parallel test thread gets its own driver instance.

🚀 Why Thread Safety Matters in Automation Frameworks

When using Cucumber + TestNG/JUnit with parallel execution enabled, multiple tests or scenarios run at the same time.

Each scenario must have its own WebDriver instance to ensure:

✅ Browsers don’t conflict

✅ Reliable parallel execution

✅ Independent test data and sessions

✅ Stable and reproducible test results

✅ Easy scalability on Selenium Grid or cloud platforms

🧩 Step-by-Step Implementation

1️⃣ Create a DriverManager Class

package driver;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.chrome.ChromeDriver;

import org.openqa.selenium.chrome.ChromeOptions;

public class DriverManager {

    // ThreadLocal variable to store WebDriver instance per thread

    private static ThreadLocal<WebDriver> tlDriver = new ThreadLocal<>();

    public static void setDriver(String browser) {

        if (browser.equalsIgnoreCase("chrome")) {

            ChromeOptions options = new ChromeOptions();

            options.addArguments("--start-maximized");

            tlDriver.set(new ChromeDriver(options));

        }

        // You can add Firefox, Edge, etc.

    }

    // Get the driver for current thread

    public static WebDriver getDriver() {

        return tlDriver.get();

    }

    // Quit driver for current thread

    public static void quitDriver() {

        if (tlDriver.get() != null) {

            tlDriver.get().quit();

            tlDriver.remove(); // Important: remove reference to prevent memory leak

        }

    }

}

2️⃣ Use DriverManager in Your Hooks Class (Cucumber)

package hooks;

import io.cucumber.java.After;

import io.cucumber.java.Before;

import driver.DriverManager;

public class Hooks {

    @Before

    public void setUp() {

        // You can read browser name from config or Cucumber tag

        DriverManager.setDriver("chrome");

        DriverManager.getDriver().get("https://example.com");

    }

    @After

    public void tearDown() {

        DriverManager.quitDriver();

    }

}

✅ This ensures that:

Before each scenario, a new driver is created for that thread.

After each scenario, the driver is quit and cleaned up safely.

3️⃣ Use It in Your Page Objects or Step Definitions

Instead of passing the driver around manually:

WebDriver driver = DriverManager.getDriver();

driver.findElement(By.id("username")).sendKeys("admin");

or in your Page Object class:

public class LoginPage {

    WebDriver driver = DriverManager.getDriver();

    public void enterUsername(String user) {

        driver.findElement(By.id("username")).sendKeys(user);

    }

}

✅ This way, every thread automatically uses its own isolated WebDriver.

4️⃣ Enable Parallel Execution in Cucumber

In your TestNG runner or JUnit runner, enable parallel execution:

@CucumberOptions(

    features = "src/test/resources/features",

    glue = {"stepdefs", "hooks"},

    plugin = {"pretty", "html:target/cucumber-report.html"}

)

public class TestRunner extends AbstractTestNGCucumberTests {

    @Override

    @DataProvider(parallel = true)

    public Object[][] scenarios() {

        return super.scenarios();

    }

}

💡 The parallel = true makes each scenario run in its own thread.

Because your WebDriver is thread-local, there’s no conflict.

⚠️ Common Mistakes to Avoid

❌ Declaring WebDriver driver as a static variable → causes cross-thread conflicts

❌ Not calling tlDriver.remove() → memory leaks

❌ Using the same test data file for write operations → need thread-safe data access

❌ Mixing implicit and explicit waits globally across threads

🧩 Optional: Thread-Safe Utilities

If you have helper classes like WaitUtils, make them thread-safe too by always using the current driver:

public class WaitUtils {

    public static WebElement waitForVisible(By locator) {

        WebDriver driver = DriverManager.getDriver();

        WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));

        return wait.until(ExpectedConditions.visibilityOfElementLocated(locator));

    }

}

✅ Final Takeaway:

Thread safety ensures each test thread runs in isolation with its own browser instance.

Using ThreadLocal makes your Selenium + Cucumber framework stable, scalable, and ready for parallel execution.

Comments

Popular posts from this blog

How to Manage Test Execution Across Different Browsers and Environments (QA, UAT, Staging)

Purpose of the StepData Class in Selenium + Java Cucumber Automation Framework