Generic Selenium WebDriver Grid Configuration Handling to use BrowserStack, Saucelabs and TestingBot

Image taken from page 103 of ‘Der Beobachter. Allgemeine Anleitung zu Beobachtungen über Land und Leute.

In my Selenium WebDriver with Java course I have a Driver.java abstraction class.

I use an abstraction class so that in the @Test methods we make a call to Driver.get rather than individual firefox or chrome drivers, or RemoteDrivers

And the configuration of this is adjusted via the actual code, or a mix of environment variables and properties.

I was looking at some Mac Grid issues that I was helping Brian Long investigate. And I realised that my Generic Grid handling wasn’t as Generic as it needed to be to support my debugging.

I amended my code so that I could configure my Driver.java with either environment variables or properties:

  • Environment Variables are handy because they are system wide and you can configure them on the machine, but they are a bit of a pain to work with because you have to restart the console and IDE when you change them.
  • Properties are handy because you can pass them into mvn test jobs with -D parameters e.g. mvn test -Dparam=value and this makes it very easy to use them in CI

I primarily use IntelliJ and in the Run \ Edit Configuration section you can override environment variables, and pass in -D params so it is possible to work with these in the IDE.

I find it easier to override environment variables in the IDE because the GUI makes it easy to amend them in a list.

I find it easier to configure the CI jobs through properties by passing them in as -D to mvn test

Therefore I amended my Driver.java code to not care if its configuration comes from environment variables or properties

e.g.

String gridBrowser = getPropertyOrEnv("WEBDRIVER_GRID_BROWSER", "firefox");
String gridBrowserVersion = getPropertyOrEnv("WEBDRIVER_GRID_BROWSER_VERSION", "");
String gridBrowserPlatform = getPropertyOrEnv("WEBDRIVER_GRID_BROWSER_PLATFORM", "");

DesiredCapabilities gridCapabilities = new DesiredCapabilities();
gridCapabilities.setBrowserName(gridBrowser);
if(gridBrowserVersion.length()>0)
    gridCapabilities.setVersion(gridBrowserVersion);
if(gridBrowserPlatform.length()>0)
    gridCapabilities.setPlatform(Platform.fromString(gridBrowserPlatform));

And getPropertyOrEnv looks as follows


/**
 * Allow setting the controls via property or environment variable
 * property takes precedence, then environment variable, then default
 */
private static String getPropertyOrEnv(String name, String theDefault){

    String theValue = System.getProperty(name);
    if(theValue == null){

        System.out.println("Could not find Property " + name);
        theValue = System.getenv(name);

        if(theValue==null){

            System.out.println("Could not find Environment Variable " + name + " using default value " + theDefault);
            theValue = theDefault;

        }else{
            System.out.println("Using Environment Variable " + name + " with value " + theValue);
        }
    }else{
        System.out.println("Using Property " + name + " with value " + theValue);
    }

    return theValue;
}

This supported my local debugging, CI and console mvn test triggering.

But as with most tasks related to automating systems, I should expect to encounter additional maintenance and workarounds. And such was the case when I tried to execute the tests against BrowserStack in addition to Saucelabs and my local grid.

Basic Saucelabs configuration uses the capabilities:

  • browser
  • platform
  • browser_version

Basic BrowserStack configuration uses the capabilities

  • os
  • os_version
  • browser
  • browser_version

And of course, with BrowserStack we want it to take screenshots to track the @Test method execution so we need to set

  • browserstack.debug=true

Since I have a ‘generic’ grid config in my Driver.java I wanted to support the extra capabilities in a more generic way.

And I still wanted to allow them to be configured via environment variables or properties.

So, I run through all the environment variables and properties looking for any name prefixed with WEBDRIVER_GRID_CAP_X_

// Allow adding any capability defined as an environment variable
// extra environment capabilities start with "WEBDRIVER_GRID_CAP_X_"

// e.g. WEBDRIVER_GRID_CAP_X_os_version XP
// e.g. WEBDRIVER_GRID_CAP_X_browserstack.debug true
Map<String, String> anyExtraCapabilities = System.getenv();
addAnyValidExtraCapabilityTo(gridCapabilities, anyExtraCapabilities.keySet());

// Now check properties for extra capabilities
Properties anyExtraCapabilityProperties = System.getProperties();
addAnyValidExtraCapabilityTo(gridCapabilities, anyExtraCapabilityProperties.stringPropertyNames());

And the magic addAnyValidExtraCapabilityTo method, looks as follows:

private static void addAnyValidExtraCapabilityTo(DesiredCapabilities gridCapabilities, Set<String> possibleCapabilityKeys) {

    String extraCapabilityPrefix = "WEBDRIVER_GRID_CAP_X_";

    for(String capabilityName : possibleCapabilityKeys){

        if(capabilityName.startsWith(extraCapabilityPrefix)){

            String capabilityValue = getPropertyOrEnv(capabilityName, "");

            if(capabilityValue.length()>0){
                String capability = capabilityName.replaceFirst(extraCapabilityPrefix,"");
                System.out.println("To Set Capability " + capability + " with value " + capabilityValue);
                gridCapabilities.setCapability(capability, capabilityValue);
            }
        }
    }
}

Nothing earth shattering there, but it allowed me to add flexibility into the config without adding too much extra code into the Driver.java class.

If I was doing this in the real world. I would have created a new configuration reader type object, rather than adding the getPropertyOrEnv into the Driver.java code, but currently, the only config I use relates to the Driver.java code and I try to avoid too many radical code changes to the course if I can avoid it.

Also, in the real world, you tend not to need to add such a large amount of flexibility. You tend to stick to one grid provider, or refactor your code into more specific abstractions.

So don’t take this code as an exemplar of how to configure your remote driver, instead look upon it as a set of quick, but pragmatic changes to add more flexibility into the code base, and perhaps you’ll see something you can re-use here.

I’ve added additional lectures into my course to explain the config I used for Saucelabs, BrowserStack and TestingBot. And uploaded all the code changes extracted here to the code in the Selenium WebDriver with Java course.

This entry was posted in Grid, Java, WebDriver. Bookmark the permalink.

2 Responses to Generic Selenium WebDriver Grid Configuration Handling to use BrowserStack, Saucelabs and TestingBot

  1. Pingback: Lessons learned from a cloud grid bug » Selenium Simplified

  2. Pingback: Testing Bits – 7/19/15 – 7/25/15 | Testing Curator Blog

Leave a Reply

Your email address will not be published. Required fields are marked *