Tweak Test Script for Parallel Run

When we run test in parallel on Selenium Grid, the test script will talk to the hub regarding what test suites to run and the hub then distribute tests on different nodes to run. The machine that runs our test script, the hub, and the nodes are in most case different machines. So our test script needs to be tweaked to address 2 things:

  • We need to tweak our code for this ‘run remotely’ scenario
  • Most of all, we need to make our script THREAD-SAFE for parallel run.

Address’run remotely’ scenario:Use ReomteWebDriver to instantiate WebDriver instance

In Selenium Grid, the machine that runs our script is different from where the browsers are, we need to use ReomteWebDriver to instantiate WebDriver instance:

final String hubURL = “ipAddress or host name of your hub machine”;
System.setProperty(“webdriver.chrome.driver”, “Path to ChromeDriver.exe”);
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities = DesiredCapabilities.chrome();
WebDriver driver = new RemoteWebDriver(new URL(hubURL),capabilities);

Address’run remotely’ scenario:Augment WebDriver to take screenshot remotely

Since the browser is running remotely on the node machines, we need to augment RemoteWebDriver before we can use the screenshot capability. Otherwise, an exception will be thrown if you fail to augment.

WebDriver driver = new RemoteWebDriver( … );
driver = new Augmenter().augment( driver );
( (TakesScreenshot)driver ).getScreenshotAs( … );

Make test script thread-safe

Make WebDriver thread-safe

WebDriver is not thread-safe. if you can serialize access to the underlying driver instance, you can share a reference in more than one thread. But this is not advisable. Instead, make your webdriver instance Thread local and instantiate one WebDriver instance for each thread:

public static ThreadLocal createThreadSafeWebDriver(final String browser)
{
ThreadLocal driver = new ThreadLocal()
{
@Override
protected WebDriver initialValue()
{
return createWebDriver(browser);
}
};
return driver;
}

private static WebDriver createWebDriver(String browser)
{ WebDriver driver = null;
if (browser.equalsIgnoreCase(“firefox”))
{
System.setProperty(“webdriver.gecko.driver”,”Path to geckodriver.exe”);
DesiredCapabilities capabilities = DesiredCapabilities.firefox();
capabilities.setCapability(“marionette”, true);
driver = new FirefoxDriver(capabilities);

}else if (browser.equalsIgnoreCase(“chrome”))
{
System.setProperty(“webdriver.chrome.driver”, “path to chromedriver.exe”);
driver = new ChromeDriver();
}
……. code for other type of browsers are skipped………

return driver;
}

Make shared variables/methods thread-safe

In general, you need to review all your code to make sure that all variables/methods that are shared by the threads are thread-safe. Here are some info that might be helful:

What are thread-safe?

  • final static variables,
  • Methods that are only utilizes local parameters,
  • Immutable like String, AtomInteger etc..
    Immutable means that once the constructor for an object has completed execution that instance can’t be altered. You can thus pass references to the object around, without worrying that someone else is going to change its contents.

What are NOT?

  • Static variables: static variables are shared between all instances of a class, thus not thread-safe
  • Instance Variables: They might be simultaneously modified by multiple threads calling un-synchronized instance methods, thus not necessarily thread-safe. For example:
    class Example {
    private int instanceVariable = 0;
    public void increment() {
    instanceVariable++;
    }
    }

    Now if two different threads call increment at the same, then you’ve got a data race – instanceVariable might increment by 1 or 2 at the end of the two methods returning.

  • methods that modify any static variable’s values

So, do go through your code several times to see:

  • Are there any resources shared in your code?
  • Are there any static variables in the classes?
  • Is there any chance that multiple threads calling unsynchronized instance methods?

How to make a variable thread-safe?

  • thread-safe lazy-initialization of the instance
    public class MySingleton {
    private static class Loader {
    static MySingleton INSTANCE = new MySingleton();
    }
    private MySingleton () {}

    public static MySingleton getInstance() {
    return Loader.INSTANCE;
    }
    }

  • Initialization on Demand:
    private static class InstanceHolder {
    private static final YourObject instance = new YourObject();
    }
    public static YourObject getInstance() {
    return InstanceHolder.instance;
    }

    This solution takes advantage of the Java memory model’s guarantees about class initialization to ensure thread safety. Each class can only be loaded once, and it will only be loaded when it is needed. That means that the first time getInstance is called, InstanceHolder will be loaded and instance will be created, and since this is controlled by ClassLoaders, no additional synchronization is necessary.

  • Wrap your variable with ThreadLocal
    The ThreadLocal class in Java enables you to create variables that can only be read and written by the same thread. Thus, even if two threads are executing the same code, and the code has a reference to aThreadLocal variable, then the two threads cannot see each other’s ThreadLocal variables.

    // thread local driver object for webdriver
    ThreadLocal driver = new ThreadLocal(){
    @Override
    protected RemoteWebDriver initialValue(){
    // can be replaced with other browser drivers
    return createRemoteDriver(hubURL, browser.get(), platform.get());
    }
    };

    This code snippet basically tells jvm to create copy of driver for each thread, so that no state is shared.

    Useful reading to understand synchronization:
    http://tutorials.jenkov.com/java-concurrency/java-memory-model.html