0

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
  • if I create 2 classes and put one method in each class along with make parallel=classes. then it works as expected. but the question is still the same how that works for 2 methods in same class. Commented Sep 30, 2022 at 18:15

1 Answer 1

0

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

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.