NashTech Blog

Implementing Parallel Test with Selenium Grid and Docker

Picture of Huyen Pham Thu
Huyen Pham Thu
Table of Contents

In the realm of software testing, the implementation of parallel test execution has gained paramount importance due to its ability to provide quicker feedback and optimize testing resources. A potent solution for achieving parallel test execution is the combination of Selenium Grid with Docker containers. This powerful amalgamation allows testers to efficiently distribute and execute tests across multiple machines or nodes simultaneously, resulting in substantial time savings and improved test coverage.

1. What is Selenium Grid

Selenium Grid enables the execution of WebDriver scripts on remote machines (virtual or physical) by routing client commands to remote browser instances. Its goal is to make it simple to perform tests on numerous machines in parallel.

Selenium Grid enables us to run tests on multiple machines in parallel and to manage different browser versions and browser configurations centrally (rather than in each individual test).

2. What is Docker

Docker is an open platform for developing, shipping, and running applications. Docker enables you to separate your applications from your infrastructure so you can deliver software quickly. With Docker, you can manage your infrastructure in the same ways you manage your applications.

3. What is Docker Compose

Docker Compose is a tool that helps you define and share multi-container applications. With Compose, you can create a YAML file to define the services and with a single command, you can spin everything up or tear it all down.

4. How to integrate Docker with Selenium Grid

Step 1. Setting up Selenium Grid with Docker

  1. Install Docker: Begin by installing Docker on your machine. Docker provides a platform to run containers that encapsulate your test environment.
  2. Create a Docker Compose file: To define your Selenium Grid setup, create a Docker Compose file (e.g., docker-compose.yml). This file specifies the configuration for the Selenium Grid hub and nodes. The hub acts as a central control point, while the nodes represent the machines and browsers where tests will be executed. Here’s an example configuration:
version: '3.8'
services:
    selenium:
      image: selenium/hub
      ports:
        - 4442:4442
        - 4443:4443        
        - 4444:4444
    chrome:
      image: selenium/node-chrome
      shm_size: 2gb
      depends_on:
        - selenium
      environment:
        - SE_EVENT_BUS_HOST=selenium
        - SE_EVENT_BUS_PUBLISH_PORT=4442
        - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
        - SE_NODE_OVERRIDE_MAX_SESSIONS=true
        - SE_NODE_MAX_SESSIONS=10
    firefox:
      image: selenium/node-firefox
      shm_size: 2gb
      depends_on:
        - selenium
      environment:
        - SE_EVENT_BUS_HOST=selenium
        - SE_EVENT_BUS_PUBLISH_PORT=4442
        - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
        - SE_NODE_OVERRIDE_MAX_SESSIONS=true
        - SE_NODE_MAX_SESSIONS=10

Let’s break down this YAML file:

version: “3.8”: This line specifies the version of the Docker Compose file syntax. The version “3.8” is used in this example.

services: This section defines the containers that make up the Selenium Grid infrastructure.

selenium: This section defines the Selenium Grid hub service.

image: Specifies the Docker image to use. In this case, the selenium/hub image is used for the hub service;  selenium/node-chrome, and selenium/node-firefox are used for the node services,  and the latest tag represents the latest version available.

ports: Published ports in exposed port: container port format.

shm_size: Use the host’s shared memory.

chrome: This section defines the Selenium Grid node service for Chrome.

firefox: This section defines the Selenium Grid node service for Firefox.

depends_on: Indicates that the node depends on the hub service. Docker Compose ensures that the hub service starts before the node.

SE_EVENT_BUS_HOST: Hub host.

SE_EVENT_BUS_PUBLISH_PORT and SE_EVENT_BUS_SUBCRIBE_PORT:  Ports 4442 and 4443 are exposed by default for Event Bus ports to help them communicate with Hub.

SE_NODE_MAX_SESSIONS: This defines the maximum number of concurrent sessions that will be allowed.

By defining the hub and node services in the Docker Compose file, you create a Selenium Grid infrastructure with a hub running on port 4444 and Chrome and Firefox nodes registered with the hub. This setup allows you to distribute test execution across multiple browsers and platforms using Selenium Grid and Docker.

Start Selenium Grid: Run the Docker Compose command (docker-compose up) in the same directory as your Docker Compose file. This command will start the Selenium Grid hub and nodes based on the defined configuration. You should see an output indicating that the containers are running.

Step 2: Configuring your test project:

Add dependencies: Make sure your test project has the necessary dependencies:

<project xmlns="<http://maven.apache.org/POM/4.0.0>" xmlns:xsi="<http://www.w3.org/2001/XMLSchema-instance>" xsi:schemaLocation="<http://maven.apache.org/POM/4.0.0> <https://maven.apache.org/xsd/maven-4.0.0.xsd>">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.nashtech</groupId>
  <artifactId>selenium-grid-docker</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <dependencies>
  	<dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-java</artifactId>
        <version>4.11.0</version>
     </dependency>
     <dependency>
        <groupId>org.testng</groupId>
        <artifactId>testng</artifactId>
        <version>7.4.0</version>
     </dependency>
  </dependencies>
</project>

Instantiate RemoteWebDriver: Instead of creating a local WebDriver instance, you need to create a RemoteWebDriver instance that connects to the Selenium Grid hub. Here’s an example:

public class BaseTest {
	protected ThreadLocal<RemoteWebDriver> driver = new ThreadLocal<RemoteWebDriver>();
        public String remote_url = "<http://localhost:4444/wd/hub>";
        protected WebDriver openMultiBrowser(String browserName) throws Exception {
                Browser browser = Browser.valueOf(browserName.toUpperCase());
                if (browser == "FIREFOX") {
                        FirefoxOptions options = new FirefoxOptions();
                        options.addArguments("--start-maximized");
                        driver.set(new RemoteWebDriver(new URL(remote_url), options));
                } else if (browser == "CHROME") {
                        ChromeOptions options = new ChromeOptions();
                        options.addArguments("--start-maximized");
                        driver.set(new RemoteWebDriver(new URL(remote_url), options));
                } else {
                        throw new RuntimeException("Cannot find browser");
                }
                driver.get().manage().timeouts().implicitlyWait(Duration.ofSeconds(2));
                driver.get().get("<https://demo.nopcommerce.com/>");
                return driver.get();
        }
}

In this example, we create a RemoteWebDriver instance by providing the URL of the Selenium Grid hub (http://localhost:4444/wd/hub). We also specify the desired capabilities for the browser (e.g., Chrome). You can create multiple instances of RemoteWebDriver for different browsers and run tests in parallel.

Create test classes: that correspond to the tests defined in the TestNG XML file. Each test class will contain test methods tagged with the desired browser using TestNG’s @Parameters annotation.

Example test class:

public class TestClass extends BaseTest {
	@Parameters("browser")
	@BeforeClass
	public void beforeClass(String browserName) throws Exception {
		driver = openMultiBrowser(browserName)
		// TO DO
		// Set up precondition here
	}

	@Test
	public void TestMethod1(Method method) {
		// Test logic using the WebDriver
    		// ...
	}
	
	@Test
	public void TestMethod2(Method method) {
		// Test logic using the WebDriver
                // ...
	}

	@Test
	public void TestMethod3(Method method) {
		// Test logic using the WebDriver
                // ...
	}
	
	@AfterClass(alwaysRun = true)
	public void afterClass() {
		// Quit the WebDriver
		if (driver != null) {
			driver.get().quit();
		}
	}
}

Running tests in parallel:

To run tests in parallel, you have a few options depending on your testing framework, you can configure parallel execution in the test configuration file. I’m using TestNG, so I created a testng.xml file. Specify the desired parallelization mode (e.g., parallel=”tests”) and ensure each test class uses a separate instance of RemoteWebDriver.

<?xml version="1.0" encoding="UTF-8"?>
<suite name="TestSuitName" parallel="tests" thread-count="2">
  <test name="Run on Chrome">
  	<parameter name="browser" value="chrome"></parameter>
    <classes>
      <class name="TestClass "/>
    </classes>
  </test>
  <test name="Run on Firefox">
  	<parameter name="browser" value="firefox"></parameter>
    <classes>
      <class name="TestClass"/>
    </classes>
  </test>
</suite>

Finally, you can run the tests using the TestNG test runner. Open a terminal or command prompt, navigate to the project’s root directory, and execute the following command:

mvn test

By following these steps, you can leverage Selenium Grid with Docker to execute your tests in parallel, maximizing the speed and efficiency of your test automation.

5. Debug Container with VNC Viewer

The main reason we want to view tests in the VNC viewer is to be able to debug any test issues that are happening inside the Docker. Typically, when tests are being triggered inside a Docker container, you cannot see anything happening inside. But, with the help of the VNC viewer, you get a visual representation of exactly what’s going on inside Docker which makes it easier to debug your test issues.

Step 1: Install a VNC client:

The one I am using in this tutorial is the RealVNC

Step 2: Use Selenium Debug images and update the port in docker-compose file:

To be able to see what’s going on inside the Docker container, you will need to use an image that has a VNC server installed. Any image that ends with -debug will have a VNC server installed.

version: '3.8'
services:
    selenium:
      image: selenium/hub
      ports:
        - 4442:4442
        - 4443:4443        
        - 4444:4444
    chrome:
      image: selenium/node-chrome-debug
      shm_size: 2gb
      depends_on:
        - selenium
      environment:
        - SE_EVENT_BUS_HOST=selenium
        - SE_EVENT_BUS_PUBLISH_PORT=4442
        - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
        - SE_NODE_OVERRIDE_MAX_SESSIONS=true
        - SE_NODE_MAX_SESSIONS=10
      ports:
        - 59001:5900
    firefox:
      image: selenium/node-firefox-debug
      shm_size: 2gb
      depends_on:
        - selenium
      environment:
        - SE_EVENT_BUS_HOST=selenium
        - SE_EVENT_BUS_PUBLISH_PORT=4442
        - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
        - SE_NODE_OVERRIDE_MAX_SESSIONS=true
        - SE_NODE_MAX_SESSIONS=10
      ports:
        - 59011:5900

Now it’s time to run tests in Docker, make sure your tests are pointed to port 4444 as that is what we have set up for our Docker container. Once you run your tests, you will notice the tests can be viewable in the VNC viewer?

You can view and download the example here.

Picture of Huyen Pham Thu

Huyen Pham Thu

Leave a Comment

Suggested Article

Discover more from NashTech Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading