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