0
votes

I am trying to run Selenium Webdriver tests in parallel on a single machine, using TestNG. I have 3 @Test methods, where 3 different users log in to the same application and reach the home page. I need @Test methods to run in parallel, and write to an ExtentReports report.

My problem is, despite 3 completely different methods in different classes, one of the users will be logged into 2 out of 3 of the browsers, leaving a user out.

The login method is located in a PageFactory page object class.

Here are my 3 test methods:

@Test(enabled = true, priority = 0) 
public void JohnLogin() throws Exception {
    ExtentTest t = ClientReportFactory.getTest();
    try {       
        Login objLogin = new Login(getDriver());
        String username = "John";
        String password = "Password";
        objLogin.SignIn(username, password);
        HomePage objHomePage = new HomePage(getDriver());   
        assertTrue(objHomePage.clientName.getText().c‌​ontains("John"));
    } catch (Exception e) {
    }
}

@Test(enabled = true, priority = 1) 
public void BobLogin() throws Exception {
    ExtentTest t = ClientReportFactory.getTest();
    try {       
        Login objLogin = new Login(getDriver());
        String username = "Bob";
        String password = "Password";
        objLogin.SignIn(username, password);
        HomePage objHomePage = new HomePage(getDriver());       
        assertTrue(objHomePage.clientName.getText().c‌​ontains("Bob"));
    } catch (Exception e) {
    }
}

@Test(enabled = true, priority = 2) 
public void SamLogin() throws Exception {
    ExtentTest t = ClientReportFactory.getTest();
    try {       
        Login objLogin = new Login(getDriver());
        String username = "Sam";
        String password = "Password";
        objLogin.SignIn(username, password);
        HomePage objHomePage = new HomePage(getDriver());       
        assertTrue(objHomePage.clientName.getText().c‌​ontains("Sam"));
    } catch (Exception e) {
    }
}

So, if I pause the tests on the Homepage. I will have 2 browser windows opened as "John", one "Bob" and no "Sam"... Causing failures.

Here's the PageFactory Object's login method.

 public void SignIn(String strUsername, String strPassword) throws InterruptedException {
    WebDriverWait wait = new WebDriverWait(driver, 15); 
    username.clear();
    username.sendKeys(strUsername);
    password.clear();
    password.sendKeys(strPassword);
    submit.click();
    wait.until(ExpectedConditions.visibilityOf(homePagePanel));
}

At first I was sure the problem was in the @BeforeMethod threading (As in, the tests were in a different thread than the @Before and @After). But I don't see how that could be the case. The Base Test method successfully opens and closes 3 browsers. It just seems like the @Test methods use each other's data! But just in case, here's my @Before and @After, with my Threading code.

public class BaseTest {
    public String browser;
    private ThreadLocal<WebDriver> threadedDriver = new ThreadLocal<WebDriver>();

@BeforeMethod(alwaysRun = true)
@Parameters({ "browser"})
public void setup(String browser)throws MalformedURLException,
InterruptedException {

        WebDriver driver = null;
        if (browser.equalsIgnoreCase("Internet Explorer")) {
            System.setProperty("webdriver.ie.driver", "C:\\Selenium\\IEDriverServer.exe");
            driver = new InternetExplorerDriver();
        } else if (browser.equalsIgnoreCase("Firefox")) {
            System.setProperty("webdriver.gecko.driver", "C:\\Selenium\\geckodriver.exe");
            driver = new FirefoxDriver();
        } else if (browser.equalsIgnoreCase("chrome")) {
            System.setProperty("webdriver.chrome.driver", "C:\\Selenium\\chromedriver.exe");
            driver = new ChromeDriver();
        } else if (browser.equalsIgnoreCase("MicrosoftEdge")) {
            System.setProperty("webdriver.edge.driver", "C:\\Selenium\\MicrosoftWebDriver.exe");
            driver = new EdgeDriver();
        }
        setWebDriver(driver);
        this.browser = browser;
        ClientReportFactory.getTest(ExtentTestName, ExtentTestDescription);

baseURL = "testApp.com";
driver.get(baseURL);
        driver.manage().window().maximize();
        }

public WebDriver getDriver(){
    return threadedDriver.get();
}
public void setWebDriver(WebDriver driver) {
    threadedDriver.set(driver);
}

@AfterMethod 
public void afterMethod() {
    ClientReportFactory.closeTest(ExtentTestName, ExtentTestDescription);
    getDriver().quit();
    threadedDriver.set(null);
}

@AfterSuite
public void afterSuite() {
    ClientReportFactory.closeReport();
    if (getDriver() != null) {
        getDriver().quit();
    } else {
        System.out.println("Drivers already closed");
    }
}
2

2 Answers

2
votes

Assuming that all of your @Test methods are in different classes, I am guessing that the problem is perhaps due to the fact that your ThreadLocal variable is NOT STATIC but is an instance variable. This causes the behaviour to be per thread per instance rather than the desired behaviour viz., per thread across all instances. You can refer to this StackOverFlow thread for a better explanation on this.

You would resort to using an instance variant of ThreadLocal if and only if all your @Test methods belong to the same test class (Because now you are only trying to ensure that the class level data member WebDriver is shared in a thread safe manner across all the test methods that belong to the same test class)

So if each of your @Test methods reside in its own Test class, then please try changing:

private ThreadLocal<WebDriver> threadedDriver = new ThreadLocal<WebDriver>();

to

private static ThreadLocal<WebDriver> threadedDriver = new ThreadLocal<WebDriver>();
0
votes

You could try this.

public class DriverFactory(){

private static ThreadLocal<WebDriver> driverThread;
public WebDriver driver;

@Parameters("browser")
public WebDriver instantiateDriverObject(String browser) {
DriverFactory factory = new DriverFactory();
driver = factory.createInstance(browser); //Driver instantiation goes here
driverThread = new ThreadLocal<WebDriver>() {
    @Override
    protected WebDriver initialValue() {
    webDriverPool.add(driver);
    return driver;
    }
};
return driver;
}

public WebDriver getDriver() {
return driverThread.get();
}

}