Parallel execution in Selenium can lead to issues like WebDriver session conflicts if multiple threads try to access the same driver instance. To avoid this, we can use ThreadLocal<WebDriver>, which provides each thread its own isolated WebDriver instance.
We achieve this by:
- Creating a utility class
DriverFactorywhere we:- Initialize the driver and assign it using
ThreadLocal.set()ininitDriver(). - Retrieve the thread-safe driver using
ThreadLocal.get()ingetDriver(). - Clean up the driver using
quitDriver()andThreadLocal.remove()to avoid memory leaks.
- Initialize the driver and assign it using
Then, we use TestNG’s @BeforeMethod and @AfterMethod annotations in a BaseTest class to call these driver setup and teardown methods for every test method.
✅ DriverFactory.java
package utils;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
// import io.github.bonigarcia.wdm.WebDriverManager; // Optional for auto-setup
public class DriverFactory {
// ThreadLocal variable for WebDriver
private static final ThreadLocal<WebDriver> tlDriver = new ThreadLocal<>();
// Initialize WebDriver and set into ThreadLocal
public static void initDriver() {
// Optional: Setup using WebDriverManager
// WebDriverManager.chromedriver().setup();
WebDriver driver = new ChromeDriver();
tlDriver.set(driver);
}
// Get driver from ThreadLocal
public static WebDriver getDriver() {
return tlDriver.get();
}
// Clean up after test
public static void quitDriver() {
WebDriver driver = getDriver();
if (driver != null) {
driver.quit();
tlDriver.remove();
}
}
}
We are going to call initDriver and quitDriver methods of DriverFactory from BaseTest using BeforeMethod and AfterMethod annotations of testng from BaseTest with the following script
✅ BaseTest.java
package tests;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.AfterMethod;
import utils.DriverFactory;
public class BaseTest {
@BeforeMethod
public void setUp() {
DriverFactory.initDriver();
}
@AfterMethod
public void tearDown() {
DriverFactory.quitDriver();
}
}
Created SampleTest class extending BaseTest with different tests to call driver with the following script
✅ SampleTest.java
package tests;
import org.openqa.selenium.WebDriver;
import org.testng.annotations.Test;
import utils.DriverFactory;
public class SampleTest extends BaseTest {
@Test
public void testGoogleSearch() {
WebDriver driver = DriverFactory.getDriver();
driver.get("https://www.google.com");
System.out.println("Page Title is: " + driver.getTitle());
}
@Test
public void testBingSearch() {
WebDriver driver = DriverFactory.getDriver();
driver.get("https://www.bing.com");
System.out.println("Page Title is: " + driver.getTitle());
}
}
testng.xml with specifying SampleTest for execution with the following xml
✅ testng.xml for Parallel Execution
<suite name=”Parallel Suite” parallel=”methods” thread-count=”2″>
<test name=”Parallel Tests”>
<classes>
<class name=”tests.SampleTest”>
</classes>
</test>
</suite>
✅ Notes & Best Practices
- Use
ThreadLocal.remove()inquitDriver()to prevent memory leaks. - Use
WebDriverManager(optional) to avoid manually setting the driver path. - You can extend
DriverFactoryto support multiple browsers using parameters. - Ensure the
chromedriverbinary is available in your system PATH or configured properly.