Reputation: 17
I've seen many questions regarding this topic but none seem to pertain to what I am looking for. I am trying to run parallel methods under a single class using the @BeforeMethod
to start up the browser, and @AfterMethod
to tear down the browser. For this example, let's just say it is a single class file that had several test methods inside of it. I wish to run 4 of them at the same time in different browsers. We are using Groovy but a Java answer would suffice.
XML:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="sampleSuite" thread-count="4" preserve-order="true" parallel="methods">
<test name="sample">
<classes>
<class name="tests.some.class.MyClassFile"/>
</classes>
</test>
</suite>
DriverFactory class that is in charge of simply creating and returning a browser:
private static WebDriver createWebDriver() {
String property = System.getProperty("driver", "chrome")
def capabilities
if (property == "chrome") {
capabilities = DesiredCapabilities.chrome()
} else if (property == "firefox") {
System.setProperty("webdriver.gecko.driver", "/usr/local/Cellar/geckodriver/0.16.1/bin/geckodriver")
capabilities = DesiredCapabilities.firefox()
}
def url = System.getProperty("seleniumServerUrl", "http://localhost:4444/wd/hub")
if (!url) {
throw new IllegalStateException("No 'seleniumServerUrl' system property set")
}
def webDriver = new RemoteWebDriver(new URL(url), capabilities)
webDriver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS)
webDriver.manage().window().setSize(new Dimension(1920,1080))
Runtime.addShutdownHook {
try {
webDriver.quit()
} catch (ignore) {}
}
webDriver
}
For threading, also in DriverFactory class:
static final WebDriver createStackWebDriver(){
ThreadLocal<WebDriver> webDriver = new ThreadLocal<WebDriver>(){
protected WebDriver initialValue(){
createWebDriver()
}
}
webDriver.get()
}
Now I have a TestPlatform class that is the central "set up and tear down" piece of the architecture. All of my class files that contain the @Test
methods extends this TestPlatform class:
class TestPlatform {
WebDriver webDriver
@BeforeMethod
void startUpWebBrowser(){
webDriver = DriverFactory.createStackWebDriver()
}
@AfterMethod
void quitWebDriver(){
webDriver.quit()
}
}
When we run the XML file, what we see is 4 instances of startUpWebBrowser()
, one out of the 4 would "pass" and continue the test, and it will promptly open up 4 methods out of one instance of startUpWebBrowser()
, while the other 3 just stay "hanging". We need it to open up one browser in each startUpWebBrowser()
instance, instead of 4 browsers in one instance.
Our current workaround is to wrap EACH @Test
method inside its own class, and change the @BeforeMethod
into @BeforeClass
as well as the afters. This somehow works but is extremely ugly on the reporting side of it, and there are other few drawbacks to this way.
Is there any solution to this issue without doing an overhaul of the current architecture? Am I just missing something very simple?
Upvotes: 0
Views: 3064
Reputation: 861
Seems like a ThreadLocal
misusing. I'd use the following snippet:
private static final ThreadLocal<WebDriver> DRIVER_CONTAINER = new ThreadLocal<>();
@BeforeMethod
void startUpWebBrowser(){
DRIVER_CONTAINER.set(createWebDriver());
}
@AfterMethod
void quitWebDriver(){
ofNullable(getDriver()).ifPresent(WebDriver::quit);
DRIVER_CONTAINER.remove();
}
public static WebDriver getDriver() {
return DRIVER_CONTAINER.get();
}
Or just move driver management piece into IInvokedMethodListener
.
Upvotes: 1
Reputation: 14736
The problem lies in your Test class TestPlatform
wherein you are saving a webdriver instance variable into a class level data member WebDriver webDriver
.
So when two or more @Test
methods run in parallel, all of them are literally trying to assign the webdriver instance to the same webDriver
instance.
Please fix it as below.
class TestPlatform {
@BeforeMethod
void startUpWebBrowser(){
DriverFactory.createStackWebDriver()
//Access webdriver instances only via DriverFactory.createStackWebDriver()
}
@AfterMethod
void quitWebDriver(){
DriverFactory.createStackWebDriver().quit()
}
}
Upvotes: 1