Background: Now using java JUnit4, willing to migrate to JUnit5 or TestNG.
Current state: Having 100+ Selenium tests. Most of them are repeated via @RunWith(Parameterized.class) in Junit4. (I.e. creating multiple instances of the test class based on the provided set of parameters, typically combinations of browser type + user identity.) Sharing a limited set of about 12 users.
Limitation: the tested application prevents the same user being logged in on multiple places simultaneously. So if the user logs in the application in some test running in one thread, it causes the same user being immediately logged off in another test running in another thread at the same moment.
Question: Is there any recommended way how to manage thread safety when tests executed in parallel can't share some resource? Or how to enforce those tests using the same resource are executed within the same thread?
Thanks for ideas.
Here is the simplified example of some solution I found with TestNG so far...:
public abstract class BaseTestCase {
protected static ThreadLocal<WebDriver> threadLocalDriver = new ThreadLocal<>();
protected String testUserName;
private static final Set<String> inUse = new HashSet<>();
public BaseTestCase(WebDriver driver, String testUserName) {
threadLocalDriver.set(driver);
this.testUserName = testUserName;
}
private boolean syncedAddUse(@NotNull String key){
synchronized (inUse){
return inUse.add(key);
}
}
private boolean syncedRemoveUse(@NotNull String key){
synchronized (inUse) {
return inUse.remove(key);
}
}
@DataProvider(parallel = true)
public static Object[][] provideTestData() {
//load pairs WebDriver+user from config file. E.g.:
//Chrome + chromeUser
//Chrome + chromeAdmin
//Firefox + firefoxUser
//etc...
}
@BeforeMethod
public void syncPoint() throws InterruptedException {
while( !syncedAddUse(testUserName) ){
//Waiting due the testUserName is already in use at the moment.
Thread.sleep(1000);
}
}
@AfterMethod
public void leaveSyncPoint(){
syncedRemoveUse(testUserName);
}
}
So I can have many test classes like:
public class TestA extends BaseTestCase {
@Factory(dataProvider = "provideTestData")
public TestA(WebDriver webDriver, String testUserName) {
super(webDriver, testUserName);
}
public void someTest() {
WebDriver driver = threadLocalDriver.get();
threadLocalDriver.get().navigate().to("http://myPage.example.com");
logintoMyPageWithUser(driver, testUserName);
doSomeStuffOnPage(driver);
logoutUserFromPage(driver);
}
...
}
And all the tests are launched via testNG.xml like this:
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="sample suite" verbose="1" parallel="instances" thread-count="20" data-provider-thread-count="10">
<test name="sample test" >
<packages>
<package name="com.path_to_package_with_example" />
</packages>
</test>
</suite>
This solution kid of works. However, I hate the Thread.sleep() there. It creates many threads and most of them keep waiting for each other. I would prefer to line up all the tests using the same user to the same thread and minimize the wait time.