Careeroza
BaseTest and DriverFactory
DriverFactory With ThreadLocal
TestNG can run tests in parallel threads. A single static WebDriver would be shared by all threads and tests would type into each other's browsers. ThreadLocal<WebDriver> gives each thread its own driver.
public final class DriverFactory {
private static final ThreadLocal<WebDriver> DRIVER = new ThreadLocal<>();
private DriverFactory() {}
public static void initDriver(String browser, boolean headless) {
WebDriver driver = switch (browser.toLowerCase()) {
case "firefox" -> {
FirefoxOptions o = new FirefoxOptions();
if (headless) o.addArguments("-headless");
yield new FirefoxDriver(o);
}
case "edge" -> new EdgeDriver();
default -> {
ChromeOptions o = new ChromeOptions();
if (headless) o.addArguments("--headless=new");
o.addArguments("--window-size=1920,1080");
yield new ChromeDriver(o); // Selenium Manager finds chromedriver
}
};
DRIVER.set(driver);
}
public static WebDriver getDriver() { return DRIVER.get(); }
public static void quitDriver() {
if (DRIVER.get() != null) {
DRIVER.get().quit();
DRIVER.remove(); // avoid memory leaks in thread pools
}
}
}BaseTest
public abstract class BaseTest {
protected WebDriver driver;
@BeforeMethod(alwaysRun = true)
@Parameters({"browser"})
public void setUp(@Optional("chrome") String browser) {
DriverFactory.initDriver(browser, ConfigReader.getBoolean("headless"));
driver = DriverFactory.getDriver();
driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(30));
driver.get(ConfigReader.get("baseUrl"));
}
@AfterMethod(alwaysRun = true)
public void tearDown() {
DriverFactory.quitDriver();
}
}Note: alwaysRun = true makes the teardown run even when the test failed or a group filter is used, so no browser is left open on the CI machine.
ThreadLocal in Plain Java
Example
Three threads each store their own "driver" name. Results are collected and printed in a fixed order:
import java.util.*;
import java.util.concurrent.*;
public class Main {
static final ThreadLocal<String> DRIVER = new ThreadLocal<>();
public static void main(String[] args) throws Exception {
ExecutorService pool = Executors.newFixedThreadPool(3);
List<Future<String>> results = new ArrayList<>();
for (String test : List.of("LoginTest", "SearchTest", "CartTest")) {
results.add(pool.submit(() -> {
DRIVER.set("ChromeDriver for " + test); // set in this thread only
Thread.sleep(50); // other threads run meanwhile
String seen = test + " sees -> " + DRIVER.get();
DRIVER.remove();
return seen;
}));
}
for (Future<String> f : results) System.out.println(f.get());
pool.shutdown();
System.out.println("main thread sees -> " + DRIVER.get());
}
}Output
LoginTest sees -> ChromeDriver for LoginTest SearchTest sees -> ChromeDriver for SearchTest CartTest sees -> ChromeDriver for CartTest main thread sees -> null