Search code examples
selenium-webdrivertestngtestng-eclipseselenium-java

Invalid session id when running selenium framework in parallel mode


I get the following when running my Selenium framework in parallel mode using TestNG.

org.openqa.selenium.NoSuchSessionException: invalid session id

The issue only happens in parallel mode and when it runs in serial mode I have no issues what so ever.

Base Test

package training.testComponents;

import java.io.FileInputStream;
import java.io.IOException;
import java.time.Duration;
import java.util.Properties;

import org.openqa.selenium.Dimension;
import org.openqa.selenium.Point;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;

import training.pageobjects.CartPage;
import training.pageobjects.CheckOutPage;
import training.pageobjects.HomePage;
import training.pageobjects.LandingPage;
import training.pageobjects.OrderConfirmationPage;
import training.pageobjects.OrdersPage;

public class BaseTest {

    public WebDriver driver;
    FileInputStream file;   
    Properties properties;
    
    final String landingURL = "https://rahulshettyacademy.com/client";
    final String landingTitle = "Let's Shop";
    final String loginSuccessMessage ="Login Successfully";
    final String loginFailMessage ="Incorrect email or password.";
    final String cartPageTitle = "My Cart"; 
    final String paymentPageTitle = "Payment Method";
    final String successfulOrderText = "THANKYOU FOR THE ORDER.";
    final int maxTimeOut = 1;
    
    public LandingPage landing;
    public HomePage home;
    public CartPage cart;
    public OrderConfirmationPage orderConfirmation;
    public CheckOutPage checkOut;
    public OrdersPage orders;
    

    void initializeDriver() throws IOException {

        properties = new Properties();
        file = new FileInputStream(
                System.getProperty("user.dir") + "\\src\\main\\java\\training\\resources\\GlobalData.properties");
        properties.load(file);
        String browserName = properties.getProperty("browser");

        if (browserName.equalsIgnoreCase("chrome")) {
            driver = new ChromeDriver();            
        }
        if (browserName.equalsIgnoreCase("firefox")) {
            driver = new FirefoxDriver();
        }
        if (browserName.equalsIgnoreCase("edge")) {
            driver = new EdgeDriver();
        }
        driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(maxTimeOut));
        driver.manage().window().setPosition(new Point(0, 0));
        driver.manage().window().setSize(new Dimension(1920, 1080));
    }
    
    @BeforeMethod(alwaysRun=true)//to run in all groups
    public void lunchBrowser() throws IOException {
        initializeDriver();
        landing = new LandingPage(driver);
        home = new HomePage(driver);
        cart = new CartPage (driver);
        orderConfirmation = new OrderConfirmationPage(driver);
        checkOut = new CheckOutPage(driver);
        orders = new OrdersPage(driver);
        
        landing.goToLandingPage(landingURL);        
        Assert.assertTrue(landing.getLandingPageTitle().equalsIgnoreCase(landingTitle));
        
    }
    @AfterMethod(alwaysRun=true)//to run in all groups
    public void closeBrowser() {
        driver.close();
    }
    public String cartPageHeader() {
        return cartPageTitle;
    }
    
    public String checkOutHeather() {
        return paymentPageTitle;
    }
    
    public String orederConfirmationText() {
        return successfulOrderText;
    }
    
    public String loginSuccessText() {
        return loginSuccessMessage;
    }   
    public String loginFailText() {
        return loginFailMessage;
    }
    
}
        landing = new LandingPage(driver);
        home = new HomePage(driver);
        cart = new CartPage (driver);
        orderConfirmation = new OrderConfirmationPage(driver);
        checkOut = new CheckOutPage(driver);
        orders = new OrdersPage(driver);
        
        landing.goToLandingPage(landingURL);        
        Assert.assertTrue(landing.getLandingPageTitle().equalsIgnoreCase(landingTitle));
        
    }
    @AfterMethod(alwaysRun=true)//to run in all groups
    public void closeBrowser() {
        driver.close();
    }
    public String cartPageHeader() {
        return cartPageTitle;
    }
    
    public String checkOutHeather() {
        return paymentPageTitle;
    }
    
    public String orederConfirmationText() {
        return successfulOrderText;
    }
    
    public String loginSuccessText() {
        return loginSuccessMessage;
    }   
    public String loginFailText() {
        return loginFailMessage;
    }
    
}

Test Class 1

package training.tests;

import java.io.IOException;

import org.testng.Assert;
import org.testng.annotations.Test;

import training.testComponents.BaseTest;

public class PlaceOrderTests extends BaseTest {

    String pass = "Password123";
    String user = "[email protected]";
    String itemName = "ZARA";

    @Test
    public void submitOrder() throws IOException {      
        String inputText = "IND";
        String wantedCountry = "india";

        // Landing page login
        Assert.assertTrue(landing.logInSecuence(user, pass).equalsIgnoreCase(loginSuccessText()));
        // Product Catalog Page
        Assert.assertTrue(home.addProductToCart(itemName));
        home.goToCart();
        // my cart page
        Assert.assertTrue(cart.getPageHeadther().equalsIgnoreCase(cartPageHeader()));
        Assert.assertTrue(cart.verifyItemInCart(itemName));
        cart.goToCheckout();
        // Checkout page
        Assert.assertTrue(checkOut.getPageHeadther().equalsIgnoreCase(checkOutHeather()));
        Assert.assertTrue(checkOut.selectCountry(inputText, wantedCountry));
        checkOut.placeOrder();
        // Order Confirmation
        Assert.assertTrue(orderConfirmation.verification().equalsIgnoreCase(orederConfirmationText()));
    }
    @Test(dependsOnMethods= {"submitOrder"})
    public void wrongProductName() throws IOException {
        // Landing page login
        Assert.assertTrue(landing.logInSecuence(user, pass).equalsIgnoreCase(loginSuccessText()));
        // Product Catalog Page
        Assert.assertTrue(home.addProductToCart(itemName));
        home.goToCart();
        // my cart page
        Assert.assertTrue(cart.getPageHeadther().equalsIgnoreCase(cartPageHeader()));
        Assert.assertFalse(cart.verifyItemInCart(itemName+"AAA"));      
    }
    @Test
    public void verifyOrder() throws IOException {
        // Landing page login
        Assert.assertTrue(landing.logInSecuence(user, pass).equalsIgnoreCase(loginSuccessText()));
        // Product Catalog Page
        Assert.assertTrue(home.addProductToCart(itemName));
        home.goToOrders();
        // my cart page
        Assert.assertTrue(orders.orderIsDisplayed(itemName));           
    }

}

Test Class 2

package training.tests;

import org.testng.Assert;
import org.testng.annotations.Test;

import training.testComponents.BaseTest;

public class ErrorValidationsTests extends BaseTest {
    
    String pass = "Password";
    String user = "[email protected]";

    @Test(groups= {"Error Handling"})
    public void wrongPassword() {
        Assert.assertTrue(landing.logInSecuence(user, pass).equalsIgnoreCase(loginFailText()));
    }
}

Page Objects

package training.pageobjects;

import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;

import training.abstractcomponents.AbstractComponents;

public class LandingPage extends AbstractComponents {
    // local variables
    WebDriver driver;

    // constructor
    public LandingPage(WebDriver driver) {
        // driver initialization
        super(driver);
        this.driver = driver;
        PageFactory.initElements(driver, this);
    }

    // Page Factory

    // UserID box
    @FindBy(id = "userEmail")
    WebElement userID;

    // Password box
    @FindBy(id = "userPassword")
    WebElement password;

    // Login button
    @FindBy(id = "login")
    WebElement loginButton;

    // Login confirmation
    @FindBy(xpath = "//*[@aria-label='Login Successfully']")
    WebElement loginToast;

    // Login failed
    @FindBy(className = "toast-error")
    WebElement loginToastFail;

    // Action Methods

    public void goToLandingPage(String landingURL) {
        driver.get(landingURL);
    }

    public String getLandingPageTitle() {
        return driver.getTitle();
    }

    public String logInSecuence(String id, String pass) {
        String text = null;
        userID.sendKeys(id);
        password.sendKeys(pass);
        loginButton.click();

        try {
            if (loginToast.isDisplayed()) {
                text = loginToast.getText();
                waitForElementInvisibility(loginToast);
            }
        } catch (NoSuchElementException e) {
            if (loginToastFail.isDisplayed() == true) {
                text = loginToastFail.getText();
                waitForElementInvisibility(loginToastFail);
            }
        }
        return text;
    }
}
package training.pageobjects;

import java.util.List;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;

import training.abstractcomponents.AbstractComponents;

public class HomePage extends AbstractComponents {

    private WebDriver driver;

    public HomePage(WebDriver driver) {
        // TODO Auto-generated constructor stub
        super(driver);
        this.driver = driver;
        PageFactory.initElements(driver, this);
    }

    // By locators
    // Displayed product name
    static By productBy = By.tagName("b");
    // Add to cart button
    static By addToCart = By.className("w-10");

    // Page Factory
    // Catalog elements
    @FindBy(className = "mb-3")
    private List<WebElement> products;

    // waiting spinner
    @FindBy(className = "ng-animating")
    private WebElement spinner;

    // added to cart toast
    @FindBy(className = "toast-success")
    private WebElement toastAdded;

    // Action Methods
    List<WebElement> getProductCatalog() {
        waitForElementVisibility(products.get(1));
        return products;
    }

    public boolean addProductToCart(String productName) {
        WebElement prod = getProductCatalog().stream()
                .filter(product -> product.findElement(productBy).getText().contains(productName)).findFirst()
                .orElse(null);
        if (prod != null) {
            prod.findElement(addToCart).click();
            waitForElementVisibility(spinner);
            // waitForElementInvisibility(spinner);
            waitForElementVisibility(toastAdded);
            waitForElementInvisibility(toastAdded);
            return true;
        } else {
            return false;
        }
    }
}
package training.pageobjects;

import java.util.List;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;

import training.abstractcomponents.AbstractComponents;

public class CartPage extends AbstractComponents {

    private WebDriver driver;
    
    // By locators
    // Items in cart
    static By cartSection = By.xpath("//ul[contains(@class,'cartWrap')]");

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

    @FindBy(xpath = "//ul[contains(@class,'cartWrap')]")
    private List<WebElement> cart;

    @FindBy(xpath = "//*[contains(@class, 'totalRow')]/button")
    private WebElement checkOutButton;

    @FindBy(xpath = "//h1[contains(text(), 'My Cart')]")
    static WebElement myCartTitle;

    // Action Methods
    List<WebElement> getCartItems() {
        waitForElementVisibility(cart.get(0));
        return cart;
    }

    public boolean verifyItemInCart(String productName) {
        boolean match = getCartItems().stream()
                .anyMatch(items -> items.findElement(cartSection).getText().contains(productName));
        return match;
    }

    public void goToCheckout() {
        checkOutButton.click();
    }

    public String getPageHeadther() {
        return myCartTitle.getText();
    }

}
package training.pageobjects;

import java.util.List;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;

import training.abstractcomponents.AbstractComponents;

public class CheckOutPage extends AbstractComponents {

    private WebDriver driver;

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

    // country text box
    @FindBy(xpath = "//input[@placeholder='Select Country']")
    private WebElement countryInput;

    // county options
    @FindBy(xpath = "//section/button")
    private List<WebElement> countryOptions;

    // place order button
    @FindBy(xpath = "//a[contains(@class, 'submit')]")
    private WebElement placeOrder;

    @FindBy(xpath = "//*[@class='payment__title']")
    static WebElement paymentTitle;

    List<WebElement> getCountryOptions(String input) {
        countryInput.sendKeys(input);
        waitForElementVisibility(countryOptions.get(0));
        return countryOptions;
    }

    public boolean selectCountry(String input, String option) {
        List<WebElement> countries = getCountryOptions(input);
        suggestiveTextBox(countries, option).get(0).click();
        return (getInputText(countryInput).equalsIgnoreCase(option));
    }

    public void placeOrder() {
        placeOrder.click();
    }

    public String getPageHeadther() {
        return paymentTitle.getText();
    }
}
package training.pageobjects;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;

import training.abstractcomponents.AbstractComponents;

public class OrderConfirmationPage extends AbstractComponents {

    private WebDriver driver;   

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

    @FindBy(tagName = "h1")
    private WebElement confirmationText;

    public String verification() {
        return confirmationText.getText();
    }
}
package training.pageobjects;

import java.util.List;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;

import training.abstractcomponents.AbstractComponents;

public class OrdersPage extends AbstractComponents{
    private WebDriver driver;
    
    @FindBy(xpath = "//tr/td[2]")
    private List<WebElement> productNames;

    public OrdersPage(WebDriver driver) {
        // TODO Auto-generated constructor stub
        super(driver);
        this.driver = driver;
        PageFactory.initElements(driver, this);
    }
    
    public boolean orderIsDisplayed(String productName) {
        boolean match;      
        match = productNames.stream().anyMatch(product->product.getText().contains(productName.toLowerCase()));
        return match;
    }       

}

Common methods for the Page Objects

package training.abstractcomponents;

import java.time.Duration;
import java.util.List;
import java.util.stream.Collectors;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import training.pageobjects.CartPage;
import training.pageobjects.HomePage;
import training.pageobjects.OrdersPage;

public class AbstractComponents {

    protected WebDriver driver;
    int maxDuration = 5;
    
    HomePage home;
    OrdersPage orders;
    CartPage cart;

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

    // Banner Elements  
    @FindBy(xpath = "//button[@routerlink = '/dashboard/']")
    WebElement homeButton;
    
    @FindBy(xpath = "//button[contains(@routerlink,'myorders')]")
    WebElement ordersButton;

    @FindBy(xpath = "//button[contains(@routerlink,'cart')]")
    WebElement cartButton;

    @FindBy(className = "fa-sign-out")
    WebElement signOutButton;

    public HomePage goToHome() {
        homeButton.click();
        home = new HomePage(driver);
        return home;
    }
    
    public OrdersPage goToOrders() {
        ordersButton.click();
        orders = new OrdersPage(driver);
        return orders;
    }
    
    public CartPage goToCart() {
        cartButton.click();
        cart = new CartPage(driver);
        return cart;
    }
    
    public void signOut() {
        signOutButton.click();
    }

    public void waitForElementVisibility(WebElement element) {
        WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(maxDuration));
        wait.until(ExpectedConditions.visibilityOf(element));
    }

    public void waitForElementInvisibility(WebElement element) {
        WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(maxDuration));
        wait.until(ExpectedConditions.invisibilityOf(element));
    }

    public List<WebElement> suggestiveTextBox(List<WebElement> elements, String option) {
        return elements.stream().filter(country -> country.getText().equalsIgnoreCase(option))
                .collect(Collectors.toList());

    }

    public String getInputText(WebElement countryInput) {
        return countryInput.getAttribute("value");
    }

}

TestNG XML file

?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Suite" parallel="methods">
  <test thread-count="5" name="Place Order Tests">
    <classes>
      <class name="training.tests.PlaceOrderTests"/>      
    </classes>
  </test> <!-- Test -->
  <test thread-count="5" name="Login Error Validations Tests">
    <classes>      
      <class name="training.tests.ErrorValidationsTests"/>
    </classes>
  </test> <!-- Test -->
</suite> <!-- Suite -->

I have tried changing the driver to static in both the base class and the PO I even tried with ThreadLocal, but wasn't able to make it work.

If anyone wants the full code, it can be found here

https://github.com/MReyes-1/seleniumTraining/tree/main/SeleniumFramework

Looks like there is something with the driver initialization but not sure what


Solution

  • The problem lies in your code training.testComponents.BaseTest

    • You have multiple test methods that would be relying on the driver object that gets initialised via your @BeforeMethod backed by the method training.testComponents.BaseTest#lunchBrowser
    • Your driver object is a static data member.
    • When you run @Test1 methods in parallel, then the @BeforeMethod also run in parallel causing multiple @BeforeMethod methods to try and set different WebDriver objects to the same driver.

    To fix this, you would need to use ThreadLocal container to hold your WebDriver objects.

    You can read more about how to work with ThreadLocal and how to go about building this in detail in my blog here

    Sometime back I built a simple library that can basically get this done for you.

    Please refer to autospawn for more details