Implementing Parallel Test Execution with Selenium Grid with Docker

Ram
9 min readJul 2, 2023

--

Selenium Grid with Docker

Parallel Test Execution with Selenium Grid with Docker

Introduction:

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.

Selenium Grid serves as a pivotal tool that facilitates test execution on diverse machines and browsers in parallel. It acts as a central hub, managing and orchestrating the execution of tests across multiple nodes. By harnessing the capabilities of Selenium Grid, testers can seamlessly perform cross-browser testing and parallel test execution.

Complementing Selenium Grid, Docker is a versatile platform that facilitates the creation and management of lightweight and isolated containers. These containers offer a self-contained environment equipped with all the necessary dependencies required for executing tests, simplifying the setup and maintenance of test environments. The portable nature of Docker containers enables hassle-free deployment on any machine equipped with Docker.

By combining Selenium Grid with Docker, testers can scale their test execution capabilities by creating multiple containers and distributing tests across them. This setup enables the parallel execution of tests on diverse browsers, operating systems, and machines, thereby enhancing test coverage and significantly reducing the overall test execution time.

To implement parallel test execution with Selenium Grid and Docker, it is imperative to establish a Selenium Grid hub and multiple Selenium Grid nodes, each operating within Docker containers. The hub serves as a central control point, receiving test requests and efficiently allocating them to available nodes for execution. The nodes, operating within Docker containers, provide the requisite browsers and operating systems necessary for test execution.

Leveraging Docker containers facilitates the swift provisioning and deprovisioning of test environments, eliminating the need for manual configuration and setup. Additionally, Docker containers ensure the isolation of tests, preventing conflicts that may arise when multiple tests are executed concurrently on different nodes.

The implementation of parallel test execution with Selenium Grid and Docker empowers testers to achieve faster feedback cycles, improved test coverage, and enhanced efficiency in their testing processes. It enables the concurrent execution of tests across a range of browsers and environments, thereby diminishing the time required for comprehensive testing.

In conclusion, the fusion of Selenium Grid with Docker provides a robust solution for parallel test execution. It allows testers to harness the potential of distributed testing and containerization, resulting in accelerated testing processes and superior software quality. By embracing this setup, organizations can elevate the quality of their software, expedite application delivery, and ensure an impeccable user experience.

The process involves two main steps:

setting up Selenium Grid using Docker and configuring your test project to execute tests in parallel.

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"
services:
hub:
image: selenium/hub:latest
ports:
- "4444:4444"
environment:
- GRID_BROWSER_TIMEOUT=300
- GRID_TIMEOUT=300
chrome:
image: selenium/node-chrome:latest
depends_on:
- hub
environment:
- HUB_HOST=hub
firefox:
image: selenium/node-firefox:latest
depends_on:
- hub
environment:
- HUB_HOST=hub

Let’s break down this YAML file:

  • version: "3": This line specifies the version of the Docker Compose file syntax. The version "3" is used in this example.
  • services: This section defines the services (containers) that make up the Selenium Grid infrastructure.
  • hub: This section defines the Selenium Grid hub service.
  • image: selenium/hub:latest: Specifies the Docker image to use for the hub service. In this case, the selenium/hub image is used, and the latest tag represents the latest version available.
  • ports: - "4444:4444": Maps the host machine's port 4444 to the container's port 4444. This allows external connections to the Selenium Grid hub.
  • environment: - GRID_BROWSER_TIMEOUT=300 - GRID_TIMEOUT=300: Sets environment variables for the hub container. In this example, GRID_BROWSER_TIMEOUT and GRID_TIMEOUT are set to 300 seconds, increasing the browser and general timeout values.
  • chrome: This section defines the Selenium Grid node service for Chrome.
  • image: selenium/node-chrome:latest: Specifies the Docker image to use for the Chrome node service. Here, the selenium/node-chrome image with the latest tag is used.
  • depends_on: - hub: Indicates that the Chrome node depends on the hub service. Docker Compose ensures that the hub service starts before the Chrome node.
  • environment: - HUB_HOST=hub: Sets the HUB_HOST environment variable for the Chrome node to point to the hub service. This allows the node to register with the hub.
  • firefox: This section defines the Selenium Grid node service for Firefox.
  • image: selenium/node-firefox:latest: Specifies the Docker image to use for the Firefox node service. Here, the selenium/node-firefox image with the latest tag is used.
  • depends_on: - hub: Indicates that the Firefox node depends on the hub service. Docker Compose ensures that the hub service starts before the Firefox node.
  • environment: - HUB_HOST=hub: Sets the HUB_HOST environment variable for the Firefox node to point to the hub service. This allows the node to register with the hub.

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. Ensure that you’re using the latest Selenium images by using the latest tag.

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 output indicating that the containers are running.

Step 2: Configuring your test project:

  1. Add dependencies: Make sure your test project has the necessary dependencies. You’ll need the Selenium WebDriver library. You can use build tools like Maven or Gradle to manage dependencies.
  2. 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 in Java:
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import java.net.URL;

public class TestClass {
public static void main(String[] args) {
try {
DesiredCapabilities capabilities = DesiredCapabilities.chrome();
WebDriver driver = new RemoteWebDriver(new URL("http://localhost:4444/wd/hub"), capabilities);
// Perform your test actions with the WebDriver instance
driver.quit();
} catch (Exception e) {
e.printStackTrace();
}
}
}

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.

Running tests in parallel:

To run tests in parallel, you have a few options depending on your testing framework:

  • TestNG or JUnit: If you’re using a testing framework like TestNG or JUnit, you can configure parallel execution in the test configuration file (e.g., testng.xml for TestNG). Specify the desired parallelization mode (e.g., parallel="tests") and ensure that each test class uses a separate instance of RemoteWebDriver.

Maven project with TestNG as the testing framework

  1. Add Dependencies: In your Maven pom.xml file, add the following dependencies for TestNG and Selenium:
<dependencies>
<!-- TestNG -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.4.0</version>
<scope>test</scope>
</dependency>

<!-- Selenium -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.1.1</version>
</dependency>
</dependencies>

2. Configure TestNG XML File: Create a TestNG XML file (e.g., testng.xml) in the project's root directory. This XML file will define test groups and specify the desired browsers for each group.

<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd" >
<suite name="MyTestSuite">
<test name="ChromeTests">
<parameter name="browser" value="chrome" />
<classes>
<class name="com.example.TestClass1" />
<class name="com.example.TestClass2" />
</classes>
</test>
<test name="FirefoxTests">
<parameter name="browser" value="firefox" />
<classes>
<class name="com.example.TestClass3" />
<class name="com.example.TestClass4" />
</classes>
</test>
</suite>

In this example, two test groups (ChromeTests and FirefoxTests) are defined. Each group specifies the desired browser using the parameter tag, and the respective test classes are included within the classes tag.

3 . Create Test Classes:

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 classes:

package com.example;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;

public class TestClass1 {
private WebDriver driver;

@BeforeClass
@Parameters("browser")
public void setup(String browser) {
// Initialize the WebDriver based on the browser parameter
if (browser.equalsIgnoreCase("chrome")) {
// Selenium3// System.setProperty("webdriver.chrome.driver", "path/to/chromedriver");
driver = new ChromeDriver();
} else if (browser.equalsIgnoreCase("firefox")) {
//Selenium3// System.setProperty("webdriver.gecko.driver", "path/to/geckodriver");
driver = new FirefoxDriver();
}
}

@Test
public void testMethod1() {
// Test logic using the WebDriver
// ...
}

@Test
public void testMethod2() {
// Test logic using the WebDriver
// ...
}

@AfterClass
public void teardown() {
// Quit the WebDriver
if (driver != null) {
driver.quit();
}
}
}

In this example, TestClass1 contains two test methods (testMethod1 and testMethod2) that will be executed on the browsers specified in the TestNG XML file. The @Parameters annotation is used to receive the browser parameter from the XML file, and the @BeforeClass and @AfterClass annotations set up and tear down the WebDriver accordingly.

Similarly, create TestClass2, TestClass3, and TestClass4 with appropriate test methods and browser configurations.

4. Run the Tests: 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

TestNG will read the TestNG XML file, identify the test groups, and execute the test methods on the corresponding browsers configured in the XML file. The tests will be distributed across the Docker containers running in Selenium Grid based on the specified browser parameters.

By configuring the TestNG XML file and using TestNG annotations, you can control which tests run on which Docker containers in Selenium Grid. The sample project and tests provided here demonstrate how to set up and execute tests on specific browsers in Selenium Grid with Docker.

Maven project with JUnit as the testing framework

  1. Add Dependencies: In your Maven pom.xml file, add the following dependencies for JUnit and Selenium:
<dependencies>
<!-- JUnit -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.8.0</version>
<scope>test</scope>
</dependency>

<!-- Selenium -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.1.1</version>
</dependency>
</dependencies>

2. Create Test Classes: Create test classes that contain test methods. Each test class will contain test methods that are tagged with the desired browser using JUnit’s @Tag annotation.

Example test classes:

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;

@Tag("chrome")
public class TestClass1 {
private static WebDriver driver;

@BeforeAll
public static void setup() {
System.setProperty("webdriver.chrome.driver", "path/to/chromedriver");
driver = new ChromeDriver();
}

@Test
public void testMethod1() {
// Test logic using the WebDriver
// ...
}

@Test
public void testMethod2() {
// Test logic using the WebDriver
// ...
}

@AfterAll
public static void teardown() {
// Quit the WebDriver
if (driver != null) {
driver.quit();
}
}
}

In this example, TestClass1 contains two test methods (testMethod1 and testMethod2) that are tagged with @Tag("chrome"). This indicates that these tests should run on the Chrome browser. The @BeforeAll and @AfterAll annotations are used to set up and tear down the WebDriver.

Similarly, create TestClass2, TestClass3, and TestClass4 with appropriate test methods and browser configurations, using the @Tag annotation to specify the desired browsers for each test class.

3. Run the Tests: To run the tests, you can use the JUnit test runner. Open a terminal or command prompt, navigate to the project’s root directory, and execute the following command:

mvn test

JUnit will execute the test methods in the test classes based on the specified browser tags. The tests will be distributed across the Docker containers running in Selenium Grid according to the specified browser tags.

By tagging the test methods with the desired browsers using JUnit’s @Tag annotation, you can control which tests run on which Docker containers in Selenium Grid. This allows you to distribute test execution across different browsers and platforms.

Maven Surefire plugin:

If you’re using Maven, you can configure the Surefire plugin in your pom.xml file. Set the parallelization options, such as <parallel>classes</parallel> or <parallel>methods</parallel>.

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version> <!-- Use the latest version available -->
<configuration>
<parallel>classes</parallel>
<threadCount>4</threadCount> <!-- Number of parallel threads -->
</configuration>
</plugin>
</plugins>
</build>

This configuration will run test classes in parallel using multiple threads.

  • Command line execution: If you prefer running tests from the command line, you can use tools like Maven or JUnit’s command line runners. For example, with Maven, you can execute mvn test -Dtest=TestClass1,TestClass2 to run specific test classes in parallel.

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.

--

--