How to perform parallel execution in selenium using ThreadLocal and PageFactory concepts

864 Views Asked by At

I am using Thread Local driver but still not able to achieve thread-safety while parallel execution.

  • 2 chrome browser launched
  • both click registration link
  • then only one browser click on login and same browser again try to click on login link and get error
  • other browser window did not click on login at all

My Driver factory class is

public class DriverFactory {

//Singleton design Pattern
//private constructor so that no one else can create object of this class
private DriverFactory() {
    
}

private static DriverFactory instance  = new DriverFactory();

public static DriverFactory getInstance() {
    return instance;
}


//factory design pattern --> define separate factory methods for creating objects and create objects by calling that methods
ThreadLocal<WebDriver> driver = new ThreadLocal<WebDriver>();

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

public void
setDriver(WebDriver driverParm) {
    driver.set(driverParm);
    System.out.println("Before Test Thread ID: "+Thread.currentThread().getId());
}


public void closeBrowser() {
    driver.get().quit();
    System.out.println("After Test Thread ID: "+Thread.currentThread().getId());
    driver.remove();
}
}

My Browser Factory class is

public class BrowserFactory {

//create webdriver object for given browser
public WebDriver createBrowserInstance(String browser) throws MalformedURLException {

    WebDriver driver = null;
    //RemoteWebDriver driver = null;


    if(browser.equalsIgnoreCase("Chrome")) {

        WebDriverManager.chromedriver().setup();
        System.setProperty("webdriver.chrome.silentOutput", "true");
        ChromeOptions options = new ChromeOptions();
        options.addArguments("--incognito");
        DriverFactory.getInstance().setDriver(driver);
        driver = new ChromeDriver(options);

    }else if (browser.equalsIgnoreCase("firefox")) {

        WebDriverManager.firefoxdriver().setup();
        FirefoxOptions foptions = new FirefoxOptions();
        foptions.addArguments("-private");
        
        //driver = new RemoteWebDriver(new URL("http:192.168.225.219:4444/wd/hub"), DesiredCapabilities.firefox());

        
        driver = new FirefoxDriver(foptions);

    } if (browser.equalsIgnoreCase("ie")) {

        WebDriverManager.iedriver().setup();
        InternetExplorerOptions iOptions = new InternetExplorerOptions();
        iOptions.addCommandSwitches("-private");

        driver = new InternetExplorerDriver(iOptions);
    }
    return driver;
}
}

My TestBase class is

public class TestBase extends ActionEngine {
public WebDriver driver;
public BrowserFactory browserFactory;
String browserName = null;
//static ExtentReports extent = ExtentManager.getInstance();

/*public WebDriver getDriver() {
    driver=DriverFactory.getInstance().getDriver();
    return driver;
}*/


@BeforeMethod
public void LaunchApplication() throws Exception {
    browserName = PropertiesOperations.getPropertyValueByKey("browser");
    browserFactory = new BrowserFactory();
    DriverFactory.getInstance().setDriver(browserFactory.createBrowserInstance(browserName));
    //driver = browserFactory.initBrowser(browserName);
    //driver = DriverFactory.getInstance().getDriver();
    String url = PropertiesOperations.getPropertyValueByKey("url");
    DriverFactory.getInstance().getDriver().get(url);
    Thread.sleep(5000);
    DriverFactory.getInstance().getDriver().manage().window().maximize();
    System.out.println("Browser maximized");
    DriverFactory.getInstance().getDriver().manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);

}

@AfterMethod
public void tearDown() {
    DriverFactory.getInstance().closeBrowser();
}

//@AfterMethod
public void assignDevice() {
    ExtentFactory.getInstance().getExtent().assignDevice(browserName);
}

//@AfterMethod
public void assignAuthor() {
    ExtentFactory.getInstance().getExtent().assignAuthor("Mayank Mishra");

}

}

TestNG.xml

<suite name="Demo Web App test suite" parallel="methods" thread-count="2" >
<listeners>
    <listener
        class-name="reusableComponents.ListenersImplementation" />
    <listener
        class-name="reusableComponents.TestRetryAnalyzerListener" />
</listeners>
<test name="LoginTests">
    <classes>
        <class name="Tests.LoginTest" />
    </classes>
</test> <!-- Test -->
    <!--<test name="DataDriven Tests">
    <classes>
    <class name="Tests.TestCase" /> 
    </classes>
</test>--> <!-- Test -->
</suite> <!-- Suite -->

Page class is

public class RegistrationPage {
private WebDriver driver;

//Asserssion asserssion;

public RegistrationPage(WebDriver driver) {
    //driver=DriverFactory.getInstance().getDriver();
    this.driver = driver;
    PageFactory.initElements(driver, this);
}

@FindBy(linkText = "ACCOUNT")
private WebElement accountLink;
@FindBy(linkText = "Register")
private WebElement registerLink;
@FindBy(linkText = "Log In")
private WebElement loginLink;
@FindBy(xpath = "//h3[contains(text(),'Contact Information')]/following-sibling::a")
private WebElement editAccountInfo;
@FindBy(xpath = "//a[contains(text(),'Forgot Your Password?')]")
private WebElement forgotPasswordLink;
//public static Logger logger = Logger.getLogger(RegistrationPage.class.getName());

//public static Logger log = Logger.getLogger("");
public  void clickAccount() throws InterruptedException {
    Thread.sleep(5000);
    accountLink.click();
    //loginLink.click();
    //forgotPasswordLink.click();
    //loginLink.click();
    //driver=DriverFactory.getInstance().getDriver();
    //driver.findElement(By.linkText("ACCOUNT")).click();
    ExtentFactory.getInstance().getExtent().log(INFO,"Click on account link");
    //Log.info("clicked on account link");
}


public  void clickLoginLink() throws InterruptedException {
    //Log.info("Click on login link");
    Thread.sleep(5000);
    loginLink.click();
    ExtentFactory.getInstance().getExtent().log(INFO,"Click on login link");
}

public void clickForgotPasswordLink() throws InterruptedException {
    //Log.info("Click on login link");
    Thread.sleep(5000);
    forgotPasswordLink.click();
    ExtentFactory.getInstance().getExtent().log(INFO,"Click on forgot password link");
}


}

and my testclass is

public class LoginTest extends TestBase {
LoginPage loginPage;
RegistrationPage registrationPage;
ExcelOperations excel = new ExcelOperations("validLogin");
ExcelOperations excel2 = new ExcelOperations("invalidLogin");


//Dataprovider method --> return object array
@DataProvider(name = "validLogin")
public Object[][] testDataSupplier1() throws Exception {
    Object[][] obj = new Object[excel.getRowCount()][1];
    for (int i = 1; i <= excel.getRowCount(); i++) {
        HashMap<String, String> testData = excel.getTestDataInMap(i);
        obj[i - 1][0] = testData;
    }
    return obj;

}

@DataProvider(name = "invalidLogin")
public Object[][] testDataSupplier2() throws Exception {
    Object[][] obj = new Object[excel2.getRowCount()][1];
    for (int i = 1; i <= excel2.getRowCount(); i++) {
        HashMap<String, String> testData = excel2.getTestDataInMap(i);
        obj[i - 1][0] = testData;
    }
    return obj;

}

@BeforeMethod
public void loadClass() {
    //loginPage = PageFactory.initElements(DriverFactory.getInstance().getDriver(), LoginPage.class);
    registrationPage = PageFactory.initElements(DriverFactory.getInstance().getDriver(), RegistrationPage.class);
}

@Test(dataProvider = "invalidLogin", description = "login with invalid password")
public void loginTest_01(Object obj1) {
    try {
        System.out.println("in method1
        registrationPage.clickAccount();
        System.out.println("clicking on login link");
        registrationPage.clickLoginLink();
       
    } catch (Exception e) {
        System.out.println(e.getMessage());
        Assert.fail("Cant do login");
    }

}

@Test(dataProvider = "validLogin", description = "login with valid password")
public void loginTest_02(Object obj2) {
    try {
        System.out.println("in method2");
        registrationPage.clickAccount();
        System.out.println("clicking on login link");
        Thread.sleep(5000);
        registrationPage.clickLoginLink();
       
    } catch (Exception e) {
        System.out.println(e.getMessage());
        Assert.fail("Cant do login");
    }

}

}
1

There are 1 best solutions below

0
Krishnan Mahadevan On

The below cleaned up version of your classes should basically solve your problem

import io.github.bonigarcia.wdm.WebDriverManager;
import java.util.Optional;
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.firefox.FirefoxOptions;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.ie.InternetExplorerOptions;

public final class DriverFactory {

  private DriverFactory() {
    //defeat instantiation
  }

  private static final ThreadLocal<WebDriver> driver = new ThreadLocal<>();
  private static final String ERROR_MSG = "WebDriver instance NOT setup for current thread";

  public static WebDriver getDriver() {
    return Optional.ofNullable(driver.get())
        .orElseThrow(() -> new IllegalStateException(ERROR_MSG));
  }

  public static void setupWebDriver(String browserFlavor) {
    driver.set(createBrowserInstance(browserFlavor));
  }

  public static void closeBrowser() {
    Optional.ofNullable(driver.get())
        .orElseThrow(() -> new IllegalStateException(ERROR_MSG))
        .quit();
    driver.remove();
  }

  private static WebDriver createBrowserInstance(String browser) {
    browser = Optional.ofNullable(browser).orElse("chrome").toLowerCase();
    switch (browser) {
      case "chrome":
        WebDriverManager.chromedriver().setup();
        System.setProperty("webdriver.chrome.silentOutput", "true");
        ChromeOptions options = new ChromeOptions();
        options.addArguments("--incognito");
        return new ChromeDriver(options);
      case "firefox":
        WebDriverManager.firefoxdriver().setup();
        FirefoxOptions foptions = new FirefoxOptions();
        foptions.addArguments("-private");
        return new FirefoxDriver(foptions);
      case "ie":
        WebDriverManager.iedriver().setup();
        InternetExplorerOptions iOptions = new InternetExplorerOptions();
        iOptions.addCommandSwitches("-private");
        return new InternetExplorerDriver(iOptions);
      default:
        throw new IllegalArgumentException("Browser flavor [" + browser + "] is NOT supported");
    }
  }
}

Here's how your base class would now look like:

import java.time.Duration;
import java.time.temporal.ChronoUnit;
import org.openqa.selenium.WebDriver;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;

public class TestBase {

  @BeforeMethod
  public void LaunchApplication() {
    String browserName =  "chrome";
    DriverFactory.setupWebDriver(browserName);
    String url = "https://www.somedomain.com";
    WebDriver driver = DriverFactory.getDriver();
    driver.get(url);
    driver.manage().window().maximize();
    System.out.println("Browser maximized");
    driver.manage().timeouts().implicitlyWait(Duration.of(30, ChronoUnit.SECONDS));
  }

  @AfterMethod
  public void tearDown() {
    DriverFactory.closeBrowser();
  }
}

With the above two cleaned up classes, you now don't need an extraneous BrowserFactory.

Whenever you would need to access a WebDriver object for the current @Test annotated test method, you just need to invoke DriverFactory.getDriver()

Note: I am using the selenium apis from v4.4.0