gridopadesham

An end-to-end documentation on Grid

View the Project on GitHub

Image Courtesy: Boloji.com

«Back Home

Build and use your own capability matcher

What is a capability matcher ?

The capability matcher is what decides on which test should be routed to which node in the grid. If there aren’t any nodes that match what the user is asking for via their desired capabilities it’s upto the capability matcher to say so.

How does the capability matcher work ?

The default capability matcher of Grid2 considers the following attributes of a capability for matching :

  1. Platform
  2. Browser name
  3. version (Browser version)
  4. “applicationName” [ This is currently not documented much anywhere]

Steps to build your own capability matcher

A custom capability matcher can be built by:

1. Extending org.openqa.grid.internal.utils.DefaultCapabilityMatcher (or)

2. Implementing org.openqa.grid.internal.utils.CapabilityMatcher

It’s usually easy to build one’s own capability matcher by extending org.openqa.grid.internal.utils.DefaultCapabilityMatcher.

Lets quickly see how a typical capability matcher can look like:

public class CrazyCapabilityMatcher extends DefaultCapabilityMatcher {
    private final String crazyNodeName = "crazyNodeName";
    @Override
    public boolean matches(Map<String, Object> nodeCapability, 
                           Map<String, Object> requestedCapability) {
        boolean basicChecks = super.matches(nodeCapability, requestedCapability);
        if (! requestedCapability.containsKey(crazyNodeName)){
            // If the user didnt set the custom capability 
            // lets just return what the DefaultCapabilityMatcher
            // would return. That way we are backward compatibility and 
            // arent breaking the default behavior of the grid
            return basicChecks;
        }
        return (basicChecks && 
        nodeCapability.containsKey(crazyNodeName) && 
        nodeCapability.get(crazyNodeName).equals(requestedCapability.get(crazyNodeName)));
    }
}

Here we have defined our custom capability matcher to look for a capability named crazyNodeName and if its present then capability matching is to be done taking into consideration the value of crazyNodeName.

If this capability is not in the user’s desired capabilities, then we are to fall back to the logic of Selenium’s default capability matcher.

Wiring in your custom capability matcher

Once we have built a jar out of our custom capability matcher, it can be wired in using two ways (Here caps-matcher.jar is the jar we built containing our capability matcher):

1. Via a configuration parameter to the hub.

java -cp caps-matcher.jar:selenium-server-standalone-3.141.59.jar \
-capabilityMatcher com.rationaleemotions.matcher.CrazyCapabilityMatcher \
org.openqa.grid.selenium.GridLauncherV3 -role hub

If you are on Windows environment, then the command would be :

java -cp caps-matcher.jar;selenium-server-standalone-3.141.59.jar \
-capabilityMatcher com.rationaleemotions.matcher.CrazyCapabilityMatcher \
org.openqa.grid.selenium.GridLauncherV3 -role hub

2. Via a Hub configuration file.

Lets create a hub configuration JSON file (called hub.json) such as below :

{
    "capabilityMatcher": "com.rationaleemotions.matcher.CrazyCapabilityMatcher"
}

Now we wire it in using the below command:

java -cp caps-matcher.jar:selenium-server-standalone-3.141.59.jar \
-hubConfig hub.json \
org.openqa.grid.selenium.GridLauncherV3 -role hub

If you are on Windows environment, then the command would be :

java -cp caps-matcher.jar;selenium-server-standalone-3.141.59.jar \
-hubConfig hub.json \
org.openqa.grid.selenium.GridLauncherV3 -role hub

Since we are talking about capability matching, there’s one more additional Hub configuration parameter, related to capability matching. Its called -throwOnCapabilityNotPresent. This is a boolean parameter and is set to true by default. It can be wired in, in the same two ways as how we wired in a capabilityMatcher.

For more details on the Hub’s configuration parameters, refer here

Tip:

Capability matcher in action

Now that we have a hub up and running with our custom capability matcher, lets put it to action.

Spawn a node using the custom capability.

Lets create a node configuration JSON file (called node.json) such as below:

{
  "capabilities":
  [
    {
      "browserName": "firefox",
      "maxInstances": 5,
      "crazyNodeName": "Rambo",
      "seleniumProtocol": "WebDriver"
    }
  ]
}

Using the above node configuration file, lets start a node:

java -jar selenium-server-standalone-3.141.59.jar -role node \
-nodeConfig node.json

We now have a Java test which looks like below :

import java.net.MalformedURLException;
import java.net.URL;
 
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
 
public class CrazyNodeTest {
    RemoteWebDriver wd = null;
 
    @BeforeClass
    public void beforeClass() throws MalformedURLException {
        DesiredCapabilities dc = new DesiredCapabilities();
        dc.setBrowserName(DesiredCapabilities.firefox().getBrowserName());
        dc.setCapability("crazyNodeName", "Rambo");
        wd = new RemoteWebDriver(new URL("http://localhost:4444/wd/hub"),dc);
    }

    @Test
    public void f() {
        wd.get("http://www.facebook.com");
    }
  
    @AfterClass
    public void afterClass() {
        wd.quit();
    }
}

So now our Hub will try and do the following in addition to performing the default capability matching:

Use case #1: crazyNodeName capability is not set with any node.

Our matcher com.rationaleemotions.matcher.CrazyCapabilityMatcher finds the capability crazyNodeName, so it will try to match it with what is available with the node i.e., it will check if the value of Spiderman in the requested capabilities (from the test) is equal to the value of in the available capabilities (from the node). But here since there are no nodes with this capability, the matching fails.

It then checks what is the value for the configuration parameter -throwOnCapabilityNotPresent. If the hub was started with its value as:

Use case #2: crazyNodeName capability is available with one node with value as Spiderman.

Our matcher com.rationaleemotions.matcher.CrazyCapabilityMatcher finds the capability crazyNodeName, so it will try to match it with what is available with the node i.e., it will check if the value of Spiderman in the requested capabilities (from the test) is equal to the value of Rambo in the available capabilities (from the node). Now these two don’t match. It then checks what is the value for the configuration parameter -throwOnCapabilityNotPresent. If the hub was started with its value as:

Use case #3: crazyNodeName capability is available with one node with value as Rambo.

The test gets routed to the node.

Now lets consider a Java class such as this one:

import java.net.MalformedURLException;
import java.net.URL;
 
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
 
public class CrazyNodeTest {
    RemoteWebDriver wd = null;
 
    @BeforeClass
    public void beforeClass() throws MalformedURLException {
        DesiredCapabilities dc = new DesiredCapabilities();
        dc.setBrowserName(DesiredCapabilities.firefox().getBrowserName());
        wd = new RemoteWebDriver(new URL("http://localhost:4444/wd/hub"),dc);
    }

    @Test
    public void f() {
        wd.get("http://www.facebook.com");
    }
  
    @AfterClass
    public void afterClass() {
        wd.quit();
    }
}

For this test, in all the above listed 3 use cases, there will not be any errors thrown, because our matcher com.rationaleemotions.matcher.CrazyCapabilityMatcher just defaults back to the default capability matching provided by Selenium, since it didn’t see the custom capability crazyNodeName in the desired capabilities from the test.

«Back Home