3

GlobalVariables class holds different variables which are used across my framework one of them is WebDriver instance:

  public class GlobalVariables
        {
        public static WebDriver driver;
    //Some other static global variables required across my framework
        public GlobalVariables(String propertiesFile)
        {
        initializeVariables(propertiesFile);
        }
        public void initializeVariables(String propertiesFile)
        {
        GlobalInitializer obj=new GlobalInitializer();
        obj.initialize(String propertiesFile);
        }
   }

GlobalInitializer contains methods to initialize all the GlobalVariables:

public class GlobalInitializer extends GlobalVariables
{
public void initialize(String propertiesFile)
{
  //Some logic to read properties file and based on the properties set in it, call other initialization methods to set the global variables.
}
public void initializeDriverInstance(String Browser)
{
driver=new FireFoxDriver();
}

//Some other methods to initialize other global variables. }

I have many GetElement classes which use the driver instance to get UI control elements E.g:

public class GetLabelElement extends GlobaleVariables
{
public static WebElement getLabel(String someID)
{
return driver.findElement(By.id(someId));
}
//Similar methods to get other types of label elements.
}

public class GetTextBoxElement extends GlobaleVariables
{
public static WebElement getTextBox(String someXpath)
{
return driver.findElement(By.xpath(someXpath));
}
//Similar methods to get other types of text box elements.
}

I have other classes which perform some actions on the UI Control(This classes too use the global variables) E.g:

public class GetLabelProperties extends GlobalVariables
{
public static String getLabelText(WebElement element)
{
return element.getText();
}
}

public class PerformAction extends GlobalVariables
{
public static void setText(String textBoxName,String someText)
{
driver.findElement(someLocator(textBoxName)).setText("someText");
}
    //Some other methods which may or may not use the global variables to perform some action
}

My test class in testng looks like this:

public class TestClass
{
GlobalVariables globalObj=new GlobalVariables(String propertiesFile);
@Test(priority=0)
{
GlobalVariables.driver.get(someURL);
//Some assertion.
}
@Test(priority=1)
{
WebElement element=GetLabelElement.getLabel(someID);
String labelName=GetLabelProperties.getLabelText(element);
//Some assertion.
}
@Test(priority=2)
{
WebElement element=GetTextBoxElement.getTextBox(someXpath);
PerformAction.setText(element.getText(),someText);
//Some assertion.
}
}

I have similar multiple test classes based on scenarios. Now this tests are running fine if i am running them individually. But when i try to run them in parallel, then this tests are failing in some weird fashion. On analyzing i found out that its the static global variables which are getting initialized by each tests thus leaving the other tests to fail. Now how should i go about achieving my objective to run multiple tests parallely with minimal changes in my framework design? i have tried searching for options, and i have come across some option i.e 1) use of synchronized. 2) Create ThreadLocal instance(Note : I have tried this solution but still same issue. tests are mixing up with each other resulting in failure. I had marked the WebDriver instance as ThreadLocal and overriden the initialValue method of ThreadLocal to initialize the driver instance. Still i am not sure whether i had implemented it correctly or not.). Now i am not sure how best to implement any one of this solution in the given scenario. Any help is appreciated. TIA!

Mrunal Gosar
  • 4,595
  • 13
  • 48
  • 71
  • First I prefer a simple "naming and directory" pattern for sharing global variables, and I don't like your solution about extending from the glob var class! so with an interface(a proxy guy) you would manage the system in parallel situation. –  Sep 28 '13 at 17:37

4 Answers4

4

I have found out the solution : Use of ThreadLocal is the best solution to run tests in a huge multithreaded environment. Code snippet to use WebDriver in multithreaded environment:

public static ThreadLocal<WebDriver> driver;
driver=new ThreadLocal<WebDriver>()
                {
                    @Override
                    protected WebDriver initialValue()
                    {
                        return new FirefoxDriver(); //You can use other driver based on your requirement.
                    }
                };

Now every time a test thread is created a new browser will open. ThreadLocal will make sure that there's only one copy of static webdriver instance per thread. [NOTE: Make sure your other global variables are too ThreadLocals. In my case they were not thats why i was running into test goof up issue]. Some extra knowledge which i would like to share so that others may find it informative. In ThreadLocal whenever the ThreadLocal.get() method is called you have to make sure that there is a provision to initialize the thread local as shown above in initialValue() method or you may run into null pointer exception. Thanks everyone.

Mrunal Gosar
  • 4,595
  • 13
  • 48
  • 71
  • Can you show a more complete structure of the code? I cannot seem to make it work using ThreadLocal. I like the idea, but i misbehaves for me and spams instances of the browser, way more than there should be. So you : Declared static threadLocal. And in every constructor of the class you override the initialValue() method, right? And then you adress the webdriver through threadLocalDriver.get().findElement ... etc. ? – Mercious May 21 '15 at 08:13
  • my design is that i have a base class where initialization of the browser instances happen and then it is extended by other classes. so in the base class i do that by overriding initialvalue or making sure that before i call the get method i am calling the set method to set the value of static threadlocal instance. – Mrunal Gosar May 22 '15 at 18:38
2

If you are going to run non-parallel, then using a static webdriver member (or a instance shared between test classes by passing by reference) is fine because it is a good way to not have to close the webdriver instance between test classes. If you want to run parallel though, you need to have one instance of webdriver for EACH thread and so in that case using a static member is the WRONG way to go. Instead you need to create or pass a webdriver instance when the test case class is invoked.

Also, you are breaking a test into separate tests for each step of the test. That is very unusual and I do not see the reason why you are doing that. You could really simplify your tests by keeping all the test steps within one singe test case like people usually do.

djangofan
  • 28,471
  • 61
  • 196
  • 289
  • Based on my googling and reading in javadocs. i found out that ThreadLocal can be used to achieve this. Now correct me if i am wrong, ThreadLocal makes sure that per thread there is only one driver instance running(i.e for multiple threads multiple driver(or browser)instances are created). And driver instance of one thread will not mix up or jumble up with another instance running in another thread. right? Please note that i have a very huge application to be tested hence i will have to go by my above design where things are broken up pretty much at granular level for ease of use – Mrunal Gosar Sep 29 '13 at 03:46
  • Personally, I wouldn't use ThreadLocal because your test framework is doing the forking and thread handling (i.e. TestNG, Maven Surefire, Gradle, or whatever). If you want to write your own thread handling and test forking by hand, then you are doing so most likely either because you are not understanding proper Selenium design patterns OR you are so advanced/knowledgeable that you have a special case in mind that requires it. – djangofan Sep 29 '13 at 21:41
1

You are getting this because of how JVM handles static members and methods.

You can't have a static webdriver object if you are going to run in parallel.

Source: The automated regression system i implemented where I work - we had this issue.

ddavison
  • 28,221
  • 15
  • 85
  • 110
1

you can try something like this

public class DriverManager {

private static final ThreadLocal<WebDriver> threadLocal = new ThreadLocal<WebDriver>();

public static WebDriver getDriver() {
    return threadLocal.get();
}

public static void setDriver(WebDriver driver) {
    threadLocal.set(driver);
}

public static void closeDriver() {
    if (getDriver() != null) {
        try {
            getDriver().close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            getDriver().quit();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    threadLocal.remove();
}

}

ramkr
  • 41
  • 4