How to run tests in parallel by methods with TestNG and Selenium

Issue

The problem is following. I am using TestNG v.7.6.0 and Selenium v.4.2.2. I try to run the same test method in parallel with invocationCount and threadPoolSize.
Here is an example of my test class (AUT is "https://www.saucedemo.com/"):

import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import java.time.Duration;

public class LoginTest {

    ThreadLocal<WebDriver> driver;

    @BeforeMethod()
    public void setup() {
        WebDriverManager.chromedriver().setup();
        WebDriver webDriver = new ChromeDriver();
        webDriver.manage().window().maximize();
        webDriver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
        webDriver.get("https://www.saucedemo.com/");
        this.driver = new ThreadLocal<WebDriver>();
        this.driver.set(webDriver);
    }

    @Test(invocationCount = 2, threadPoolSize = 2)
    public void test() {
        this.driver.get().findElement(By.id("user-name")).sendKeys("standard_user");
        this.driver.get().findElement(By.id("password")).sendKeys("secret_sauce");
        this.driver.get().findElement(By.id("login-button"));
    }
}

The first test run is OK, but the second test fails with error

java.lang.NullPointerException: Cannot invoke "org.openqa.selenium.WebDriver.findElement(org.openqa.selenium.By)" because the return value of "java.lang.ThreadLocal.get()" is null

    at LoginTest.test(LoginTest.java:36)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
    at java.base/java.lang.reflect.Method.invoke(Method.java:577)
    at org.testng.internal.invokers.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:139)
    at org.testng.internal.invokers.TestInvoker.invokeMethod(TestInvoker.java:677)
    at org.testng.internal.invokers.TestInvoker.invokeTestMethod(TestInvoker.java:221)
    at org.testng.internal.invokers.MethodRunner.runInSequence(MethodRunner.java:50)
    at org.testng.internal.invokers.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:962)
    at org.testng.internal.invokers.TestInvoker.invokeTestMethods(TestInvoker.java:194)
    at org.testng.internal.invokers.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:148)
    at org.testng.internal.invokers.TestMethodWorker.run(TestMethodWorker.java:128)
    at org.testng.internal.thread.ThreadUtil.lambda$execute$0(ThreadUtil.java:58)
    at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)

So, driver is not set properly in second test. Why? How can I fix it?

If I don’t use ThreadLocal, my tests mix with each other. For example, the password from the second test run is set in password field in the first browser.
What is the proper way to work with driver in parallel by methods case? Thank you in advance

Solution

As you’re having this line in your setup this.driver = new ThreadLocal<WebDriver>(); the second test overrides threadlocal with new value when second thread is starting.

So when the first thread is trying to obtain a value it saved to threadlocal before, it fails to find that, because by that moment this.driver field refers to the object created by the second thread.

To fix that you need to initialize threadlocal right in the field declaration, so your code would look like this:

P.S. – it is also worth making that field static so that you’ll also be handling cases when there will be created new LoginTest instance for each of your test (however I’m not sure if that could be the case for TestNg).

public class LoginTest {

    static ThreadLocal<WebDriver> driver = new ThreadLocal<WebDriver>();

    @BeforeMethod()
    public void setup() {
        WebDriverManager.chromedriver().setup();
        WebDriver webDriver = new ChromeDriver();
        webDriver.manage().window().maximize();
        webDriver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
        webDriver.get("https://www.saucedemo.com/");
        driver.set(webDriver);
    }

    @Test(invocationCount = 2, threadPoolSize = 2)
    public void test() {
        driver.get().findElement(By.id("user-name")).sendKeys("standard_user");
        driver.get().findElement(By.id("password")).sendKeys("secret_sauce");
        driver.get().findElement(By.id("login-button"));
    }
}

Answered By – Alexey R.

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published