It depends.
When using a static field, there is only a single instance of that field in the JVM. It refers to one specific address in memory. Unlike the field of an object which refers to a field of a specific object and for each object there is a unique address in memory for that field.
When using a ThreadLocal
each thread has its own instance of the variable.
So by using static WebDriver driver
you have exactly one webdriver for all tests and all threads. By using static ThreadLocal<WebDriver> driver
you have exactly one webdriver for each thread, effectively sharing the webdriver between scenarios that are executed on that thread.
When executing tests in parallel there are multiple threads executing scenarios so a single webdriver would be a problem. Scenarios running in parallel would make the webdriver do different things at the same time or they'd have to wait for the webdriver to be available, making them effectively run serially again.
So if a webdriver is to be shared between scenarios and if those scenarios run in parallel you have to use a ThreadLocal
.
However it appears that are unfamiliar with programming concurrent systems. So you may want to consider a different approach. Rather then sharing the webdriver between scenarios, consider starting a new webdriver in each scenario instead. This is much safer and from a test perspective much cleaner because each scenario starts without any state from the previous scenario.
This means you now have the problem of sharing information between steps. You were using a static field to share to share the webdriver. But you can't use use static fields because there are now multiple threads running.
To solve this problem Cucumber supports dependency injection. The easiest to use is probably cucumber-pico. When using dependency injection cucumber will instantiate each step definition class for each scenario with a isolated set of dependencies.
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-picocontainer</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>
So for example when your step definitions depend on a WebDriverFactory
, cucumber will instantiate a WebDriverFactory
for you and instantiate both StepDefinition
and OtherStepDefinition
with the same factory.
public class StepDefinition {
private final WebDriverFactory webdriverFactory;
public StepDefinitions(WebDriverFactory webdriverFactory) {
this.webdriverFactory = webdriverFactory;
}
@Given("I do a thing with a webdriver")
public void useTheWebDriver() {
// get the webdriver from the factory and use it
}
}
public class OtherStepDefinition {
private final WebDriverFactory webdriverFactory; // Same instance as in StepDefinition
public OtherStepDefinition(WebDriverFactory webdriverFactory) {
this.webdriverFactory = webdriverFactory;
}
@Given("I do another thing with a webdriver")
public void useTheWebDriver() {
// get the webdriver from the factory and use it
}
}
Then in the web driver factory you keep a reference to the webdriver for use in both step definitions.
public class WebDriverFactory implements Startable {
private WebDriver driver;
public setDriver(String browser) {
// create the web driver here
}
public static synchronized WebDriver getDriver(){
return driver;
}
public void start() {
// do nothing
}
public void stop() {
// stop web driver if it was created
}
}