Skip to main content
blog title image

4 minute read - Selenium WebDriver FAQs Test Automation

How to Use Grid with a Driver Manager

Jul 21, 2015

Driver Abstractions are essential to help move between environments.

I use a Driver.java abstraction class in most of my training and on production code 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 cam 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){
    
            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.


Full Source

The full source for this is in my Webdriver Java FAQs project:

Specifically Driver.java in:

I cover the Driver Abstractions in my Selenium WebDriver with Java course.

And cover abstractions in detail in my Linkedin learning course Page Objects and Abstractions