Parallel execution of test throws the following exception when parallel=true on DataProvider. Two browser instances will open running on separate threads but only one browser instance will execute successfully and the other one gets stuck on trying to find element and throwing an exception. Please, how do I get the test to execute successfully when parallel=true in dataprovider. I'm I missing something maybe the WebDriver instance in the BasePage. Please help. Thanks.
[PoolService-1] 15:44:06,447 INFO [Default test] Close driver
PASSED: loginTest("unresticteduser@gmail.com", "123456")
FAILED: loginTest("resticteduser@gmail.com", "123456")
WARNING: WebDriverException thrown by findElement(By.xpath: //button[contains(text(), 'NO THANKS')])
org.openqa.selenium.WebDriverException: org.apache.http.conn.HttpHostConnectException: Connect to
localhost:8478 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused: connect
When parallel=false on dataprovider, the test will open one browser instance at a time as the data is fed into the test and executes successfully.
PASSED: loginTest("unresticteduser@gmail.com", "123456")
PASSED: loginTest("resticteduser@gmail.com", "123456")
Here's the code I wrote:
1) BrowserFactory
public class BrowserDriverFactory {
private ThreadLocal<WebDriver> driver = new ThreadLocal<WebDriver>();
private String browser;
private Logger log;
public BrowserDriverFactory(String browser, Logger log) {
this.browser = browser.toLowerCase();
this.log = log;
}
public WebDriver createDriver() {
// Create driver
log.info("Create driver: " + browser);
if(browser.equals("chrome")) {
System.setProperty("webdriver.chrome.driver", "src/main/resources/chromedriver.exe");
driver.set(new ChromeDriver());
}
else if(browser.equals("firefox")) {
System.setProperty("webdriver.gecko.driver", "src/main/resources/geckodriver.exe");
driver.set(new FirefoxDriver());
}
else {
System.out.println("Do not know how to start: " + browser + ", starting chrome.");
System.setProperty("webdriver.chrome.driver", "src/main/resources/chromedriver.exe");
driver.set(new ChromeDriver());
}
return driver.get();
}
}
2)BaseTest
public class BaseTest {
protected WebDriver driver;
public WebDriverWait wait;
protected Logger log;
protected FirefoxProfile profile;
protected String url = "http://www.qaclickacademy.com/";
protected String testSuiteName;
protected String testName;
protected String testMethodName;
@BeforeMethod(alwaysRun = false)
public void setUp(Method method, ITestContext ctx) {
String testName = ctx.getCurrentXmlTest().getName();
log = LogManager.getLogger(testName);
BrowserDriverFactory factory = new BrowserDriverFactory(config.getBrowser(), log);
driver = factory.createDriver();
driver.get(url);
profile=new FirefoxProfile();
// Set preferences for file type
profile.setPreference("browser.helperApps.neverAsk.openFile", "application/octet-stream");
driver.manage().window().maximize();
wait = new WebDriverWait(driver, 5);
this.testSuiteName = ctx.getSuite().getName();
this.testName = testName;
this.testMethodName = method.getName();
}
@AfterMethod(alwaysRun = true)
public void tearDown(ITestResult result) {
if(result.getStatus()==ITestResult.FAILURE) {
//capture screenshot
}
log.info("Close driver");
// Close browser
driver.quit();
}
}
3)DataProvider
@DataProvider(name="SearchProvider", parallel=true)
public Object[][] getDataFromDataprovider(){
return new Object[][]
{
{ "unresticteduser@gmail.com", "123456" },
{ "resticteduser@gmail.com", "123456" }
};
}
4)BasePage
public class BasePage {
protected WebDriver driver;
public WebDriverWait wait;
protected Logger log;
public BasePage(WebDriver driver, Logger log) {
this.driver = driver;
this.log = log;
wait = new WebDriverWait(driver, 5);
}
protected void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
protected void clickStallElement(By locator) {
try {
waitForVisibilityOf(locator, 30);
findElement(locator).click();
}
catch(org.openqa.selenium.StaleElementReferenceException ex)
{
waitForVisibilityOf(locator, 30);
findElement(locator).click();
}
}
/** Open page with given URL */
protected void openUrl(String url) {
driver.get(url);
}
/** Find element using given locator */
protected WebElement findElement(By locator) {
return driver.findElement(locator);
}
/** Find all elements using given locator */
protected List<WebElement> findAllElements(By locator) {
return driver.findElements(locator);
}
/** Click on element with given locator when its visible */
protected void click(By locator) {
waitForVisibilityOf(locator, 30);
findElement(locator).click();
log.info("Clicked WebElement");
}
...
}
5) BaseTest
public class SignInPageTest extends BaseTest {
@Test(dataProvider = "SearchProvider", dataProviderClass = DBDataProvider.class)
public void loginTest(String username, String password) {
System.err.println("Running Test=> " + this + " -> on thread [" + Thread.currentThread().getId() + "]");
log.info("Starting LogInTest # for " + username);
ClickAcadamyLandingPage home = new ClickAcadamyLandingPage(driver, log);
home.openPage();
home.closeNewsLetterPopup();
// click login
ClickAcadamyLoginPage loginPage = new ClickAcadamyLoginPage(driver, log);
loginPage = home.clickLoginBtn();
loginPage.enterEmailAddress(username);
loginPage.enterPassword(password);
ClickAcadamyDashboard dashboard = loginPage.clickLogin();
dashboard.clickLogout();
}
}
6)testng.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite" data-provider-thread-count = "2" thread-count="2" verbose="1" parallel="tests">
<listeners>
<listener class-name="com.paralleltest.basetest.TestListener"/>
</listeners>
<test name="loginTest">
<classes>
<class name="com.paralleltest.SignInPageTest"/>
</classes>
</test>
</suite>
The problem lies in your test code. Your webdriver instances are getting shared across test methods when you run tests in parallel and powered by a data provider.
Here's a fixed version of your BaseTest
and BrowserDriverFactory
to include thread safety.
import java.lang.reflect.Method;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxProfile;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
public class BaseTest {
private static final ThreadLocal<WebDriver> drivers = new ThreadLocal<>();
public WebDriverWait wait;
protected FirefoxProfile profile;
protected String url = "http://www.qaclickacademy.com/";
protected String testSuiteName;
protected String testName;
protected String testMethodName;
public WebDriver getDriver() {
return drivers.get();
}
@BeforeMethod()
public void setUp(Method method, ITestContext ctx) {
String testName = ctx.getCurrentXmlTest().getName();
WebDriver driver = BrowserDriverFactory.createDriver("firefox");
drivers.set(driver);
driver.get(url);
driver.manage().window().maximize();
wait = new WebDriverWait(driver, 5);
this.testSuiteName = ctx.getSuite().getName();
this.testName = testName;
this.testMethodName = method.getName();
}
@AfterMethod(alwaysRun = true)
public void tearDown(ITestResult result) {
getDriver().quit();
drivers.remove();
}
}
BrowserDriverFactory.java
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.firefox.FirefoxProfile;
public class BrowserDriverFactory {
public static WebDriver createDriver(String browser) {
System.err.println("Create driver: " + browser);
if (browser.equals("chrome")) {
System.setProperty("webdriver.chrome.driver", "src/main/resources/chromedriver.exe");
return new ChromeDriver();
} else if (browser.equals("firefox")) {
System.setProperty("webdriver.gecko.driver", "src/main/resources/geckodriver.exe");
FirefoxOptions options = new FirefoxOptions();
FirefoxProfile profile = new FirefoxProfile();
profile.setPreference("browser.helperApps.neverAsk.openFile", "application/octet-stream");
options.setProfile(profile);
return new FirefoxDriver(options);
}
System.out.println("Do not know how to start: " + browser + ", starting chrome.");
System.setProperty("webdriver.chrome.driver", "src/main/resources/chromedriver.exe");
return new ChromeDriver();
}
}
Now in all the test methods of test classes that extend BaseTest
whenever you need access to the driver object, you would get it by invoking getDriver()
method.
You can find a full fledge explanation and some more explanation around this in this blog of mine: https://rationaleemotions.com/parallel_webdriver_executions_using_testng/
I also created a library that abstracts out the webdriver lifecycle management and makes it even more simpler for you via a custom annotation.
Take a look at https://github.com/rationaleEmotions/autospawn