Search code examples
multithreadingparallel-processingtestngselenium-grid

NoSuchSessionException: invalid session id - TestNG parallel=classes with PageObjects


I am trying to execute 2 basic testng classes in parallel but it seems that I am doing something wrong. If I run the classes in sequential order, everything is fine.

When I run the classes in parallel I receive a NoSuchSessionException and the actions from both classes are executed only in one browser (both browsers open facebook page but the all actions are executed only on 1 browser)

9-12-2018 21:24:23 INFO  Log:21 - ===============================================
19-12-2018 21:24:23 INFO  Log:21 - ===============================================
19-12-2018 21:24:23 INFO  Log:21 - Test started.
19-12-2018 21:24:23 INFO  Log:21 - Test started.
Dec 19, 2018 9:24:26 PM org.openqa.selenium.remote.ProtocolHandshake createSession
INFO: Detected dialect: OSS
Dec 19, 2018 9:24:26 PM org.openqa.selenium.remote.ProtocolHandshake createSession
INFO: Detected dialect: OSS
beforeClass thread id = 13
beforeClass hashcode of webDriver instance = 1876669297

org.openqa.selenium.NoSuchSessionException: invalid session id
  (Driver info: chromedriver=2.43.600233 (523efee95e3d68b8719b3a1c83051aa63aa6b10d),platform=Linux 4.9.93-linuxkit-aufs x86_64) (WARNING: The server did not provide any stacktrace information)
Command duration or timeout: 0 milliseconds
Build info: version: '3.12.0', revision: '7c6e0b3', time: '2018-05-08T14:04:26.12Z'
System info: host: 'LENOVO', ip: '10.0.75.1', os.name: 'Windows 10', os.arch: 'amd64', os.version: '10.0', java.version: '10.0.2'
Driver info: org.openqa.selenium.remote.RemoteWebDriver
Capabilities {acceptInsecureCerts: false, acceptSslCerts: false, applicationCacheEnabled: false, browserConnectionEnabled: false, browserName: chrome, chrome: {chromedriverVersion: 2.43.600233 (523efee95e3d68..., userDataDir: /tmp/.org.chromium.Chromium...}, cssSelectorsEnabled: true, databaseEnabled: false, goog:chromeOptions: {debuggerAddress: localhost:40401}, handlesAlerts: true, hasTouchScreen: false, javascriptEnabled: true, locationContextEnabled: true, mobileEmulationEnabled: false, nativeEvents: true, networkConnectionEnabled: false, pageLoadStrategy: normal, platform: LINUX, platformName: LINUX, rotatable: false, setWindowRect: true, takesHeapSnapshot: true, takesScreenshot: true, unexpectedAlertBehaviour: , unhandledPromptBehavior: , version: 70.0.3538.102, webStorageEnabled: true, webdriver.remote.sessionid: e76393c8805f3970c6ad0e910d7...}

    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:488)
    at org.openqa.selenium.remote.ErrorHandler.createThrowable(ErrorHandler.java:214)
    at org.openqa.selenium.remote.ErrorHandler.throwIfResponseFailed(ErrorHandler.java:166)
    at org.openqa.selenium.remote.http.JsonHttpResponseCodec.reconstructValue(JsonHttpResponseCodec.java:40)
    at org.openqa.selenium.remote.http.AbstractHttpResponseCodec.decode(AbstractHttpResponseCodec.java:80)
    at org.openqa.selenium.remote.http.AbstractHttpResponseCodec.decode(AbstractHttpResponseCodec.java:44)
    at org.openqa.selenium.remote.HttpCommandExecutor.execute(HttpCommandExecutor.java:158)
    at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:543)
    at org.openqa.selenium.remote.RemoteWebElement.execute(RemoteWebElement.java:276)
    at org.openqa.selenium.remote.RemoteWebElement.click(RemoteWebElement.java:83)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at org.openqa.selenium.support.pagefactory.internal.LocatingElementHandler.invoke(LocatingElementHandler.java:51)
    at com.sun.proxy.$Proxy15.click(Unknown Source)
    at pageObjects.HomePage1.setUsername(HomePage1.java:26)
    at uiTests.BaseTest.logIn(BaseTest.java:129)
    at uiTests.transactions.firstClass.class1Test1(firstClass.java:16)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:131)
    at org.testng.internal.Invoker.invokeMethod(Invoker.java:658)
    at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:792)
    at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1103)
    at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:140)
    at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:122)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1135)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
    at java.base/java.lang.Thread.run(Thread.java:844)

class1 thread id = 13
class1 hashcode of webDriver instance = 1876669297
beforeClass thread id = 14
beforeClass hashcode of webDriver instance = 1321310502
class2 thread id = 14
class2 hashcode of webDriver instance = 1321310502
afterclass2 thread id = 14
afterclass2 hashcode of webDriver instance = 1321310502
afterclass1 thread id = 13
afterclass1 hashcode of webDriver instance = 1876669297

===============================================
parallelTest
Total tests run: 2, Passes: 1, Failures: 1, Skips: 0
===============================================

DriverFactory

package utils;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.testng.Assert;

import java.net.MalformedURLException;
import java.net.URL;

public class DriverFactory {
    public static WebDriver createInstance(String browserType) throws MalformedURLException {
        WebDriver driver = null;

        if (browserType.toLowerCase().contains("chrome")) {
            switch (Constants.RUNNER) {
                case "localhost":
                    driver = createLocalChromeDriver();
                    break;
                case "remote":
                    driver = createRemoteChromeDriver();
                    break;
                default:
                    Log.error("  !!! The value for Constants.RUNNER is not correct; Valid options are: localhost, remote");
                    Assert.fail();
            }
        }
        return driver;
    }

    private static WebDriver createLocalChromeDriver() {
        System.setProperty("webdriver.chrome.driver", "src/main/resources/drivers/chromedriver.exe");

        ChromeOptions options = new ChromeOptions();
        options.addArguments("start-maximized");
        options.addArguments("--incognito");

        WebDriver driver = new ChromeDriver(options);
        driver.manage().deleteAllCookies();
        return driver;
    }

    private static WebDriver createRemoteChromeDriver() throws MalformedURLException {
        ChromeOptions options = new ChromeOptions();
        options.addArguments("start-maximized");
        options.addArguments("--incognito");

        WebDriver driver = new RemoteWebDriver(new URL(Constants.SELENIUM_GRID_ADDR_LOCAL), options);
        driver.manage().deleteAllCookies();
        return driver;
    }
}

DriverManager

package utils;

import org.openqa.selenium.WebDriver;

public class DriverManager {
    private static ThreadLocal<WebDriver> webDriver = new ThreadLocal<WebDriver>();

    public static WebDriver getWebDriver() {
        return webDriver.get();
    }

    public static void setWebDriver(WebDriver driver) {
        webDriver.set(driver);
    }
}

BasePage

public class BasePage {
    public WebDriver driver;

    public static final int DEFAULT_WAIT_ELEMENT_TIMEOUT = 5;
    public static final int DEFAULT_WAIT_PAGE_TIMEOUT = 30;
    private static final String CHAR_LIST ="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";

    public void goToWebPage(String url) {
        Log.debug(" - opening URL: " + url);
        driver.get(url);
    }

    public void scrollToElement(WebElement webElement) {
        waitForElementToBeVisible(webElement, DEFAULT_WAIT_ELEMENT_TIMEOUT);
        ((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView(true);", webElement);
        waitABit(500);
    }
-----------------
}

HomePage1

public class HomePage1 extends BasePage{
    @FindBy(xpath = "//input[@id='email']")
    private WebElement txtUsername;

    @FindBy(xpath = "//input[@id='pass']")
    private WebElement txtPassword;

    @FindBy(xpath = "//input[@value='Log In']")
    private WebElement btnLogIn;

    public HomePage1(WebDriver driver) {
        this.driver = driver;
        PageFactory.initElements(driver, this);
    }

    public void setUsername(String newValue) {
        waitForElementToBeVisible(txtUsername, DEFAULT_WAIT_ELEMENT_TIMEOUT);
        scrollToElement(txtUsername);
        txtUsername.click();
        txtUsername.clear();
        txtUsername.sendKeys(newValue);
    }

    public void setPassword(String newValue) {
        waitForElementToBeVisible(txtPassword, DEFAULT_WAIT_ELEMENT_TIMEOUT);
        scrollToElement(txtPassword);
        txtPassword.click();
        txtPassword.clear();
        txtPassword.sendKeys(newValue);
    }

    public void clickLogInBtn() {
        waitForElementToBeVisible(btnLogIn, DEFAULT_WAIT_ELEMENT_TIMEOUT);
        btnLogIn.click();
    }
}

BaseTest

public class BaseTest {
    private static HomePage1 homePage1;

    @BeforeClass
    @Parameters({"env", "browserType"})
    public void beforeClass(@Optional("test") String env, @Optional("chrome") String browser) throws Exception {
        WebDriver driver = null;
        driver = DriverFactory.createInstance(browser);
        DriverManager.setWebDriver(driver);

        System.out.println("beforeClass thread id = " + Thread.currentThread().getId());
        System.out.println("beforeClass hashcode of webDriver instance = " + DriverManager.getWebDriver().hashCode());

        homePage1 = new HomePage1(driver);
    }

    protected void logIn(String user, String pass) {
        homePage1.setUsername(user);
        homePage1.setPassword(pass);
        homePage1.clickLogInBtn();
    }
}

firstClass

import org.testng.annotations.*;
import uiTests.BaseTest;
import utils.DriverManager;

public class firstClass extends BaseTest {

    @Test()
    public void class1Test1() {
        System.out.println("class1 thread id = " + Thread.currentThread().getId());
        System.out.println("class1 hashcode of webDriver instance = " + DriverManager.getWebDriver().hashCode());

        homePage.goToWebPage("https://www.facebook.com/");
        homePage.waitABit(2000);
        logIn("[email protected]", "aaa");
    }

    @AfterClass
    public void closeBrowser() {
        System.out.println("afterclass1 thread id = " + Thread.currentThread().getId());
        System.out.println("afterclass1 hashcode of webDriver instance = " + DriverManager.getWebDriver().hashCode());

        DriverManager.getWebDriver().quit();
    }
}

secondClass

import org.testng.annotations.*;
import uiTests.BaseTest;
import utils.DriverManager;

public class secondClass extends BaseTest {
    @Test
    public void class1Test1() {
        System.out.println("class2 thread id = " + Thread.currentThread().getId());
        System.out.println("class2 hashcode of webDriver instance = " + DriverManager.getWebDriver().hashCode());

        homePage.goToWebPage("https://www.facebook.com/");
        logIn("[email protected]", "bbb");
    }

    @AfterClass
    public void closeBrowser() {
        System.out.println("afterclass2 thread id = " + Thread.currentThread().getId());
        System.out.println("afterclass2 hashcode of webDriver instance = " + DriverManager.getWebDriver().hashCode());

        DriverManager.getWebDriver().quit();
    }
}

testNgG.xml

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >

<suite name="parallelTest">
    <test name="notSoParallel" parallel="classes" thread-count="2">
        <classes>
            <class name="uiTests.transactions.firstClass" />
            <class name="uiTests.transactions.secondClass" />
        </classes>
    </test>

</suite>

pom.xml

<dependency>
        <groupId>org.testng</groupId>
        <artifactId>testng</artifactId>
        <version>7.0.0-beta1</version>
    </dependency>
    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-java</artifactId>
        <version>3.12.0</version>
    </dependency>

Expectation: the 2 classes are running completely separated.

Thank you in advance for any suggestion.


Solution

  • The problem lies in your test code.

    In your class BaseTest you have declared homePage1 as a static data member. This causes all instances of BaseTest and its child classes to share the same instance of HomePage1.

    When you run sequentially the WebDriver object being persisted into HomePage1 is properly cleaned up and properly initialised via the @BeforeClass and @AfterClass.

    But when running in parallel, there's a race condition which causes the fast running test class to close the driver object and thus cause the slow running test class to end up with a driver which doesnt have a valid session.

    To fix this change private static HomePage1 homePage1;

    To private HomePage1 homePage1;

    PS: There are discrepancies in the code you shared. Your test classes are referring to an object called homePage but I couldn't find any such data member in either the test classes or in the base class BaseTest. BaseTest is only have a data member called homePage1. Not sure where the disconnect is though.