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
The problem lies in your code training.testComponents.BaseTest
driver
object that gets initialised via your @BeforeMethod
backed by the method training.testComponents.BaseTest#lunchBrowser
driver
object is a static data member.@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