Docker — Selenium | How To Integrate Selenium Grid With Docker
Testing is becoming more and more critical for validation, confidence and agility in today’s software and web development market. In parallel to its evolving, it brings challenges as well. Naturally, every new challenge comes with a new brilliant solution. For instance, once it is realized that the massive amount of features needs to be tested on different operating systems and with different browsers, then as a solution selenium grid option came into picture. Selenium grid helps to run our test cases in different operating systems and on different browsers.
However, in this article I will try to explain more about what a Docker is and how we download, install, and integrate it with the Selenium grid, so that our job will be easier than ever. This approach is also a new solution to previous challenges.
Assumptions: You have already knowledge of testing frameworks, such as TestNG, JUnit, Cucumber. You know the concept of paralell and cross-browser testing. You are also familiar with selenium grid concept. Then this article might help you to execute your test cases with Docker containers. Programming language will be Java, build management tool will be maven. I will try to cover both TestNG and Cucumber|JUnit frameworks.
Let’s start! ✍
Selenium Grid
Selenium Grid is a proxy server based on hub-nodes architecture. It facilitates easy process to run tests in parallel across multiple machines(nodes).
In Selenium Grid, one server acts as the hub that routes JSON formatted test commands to one or more registered Grid nodes. Selenium Grid allows us to run tests in parallel on multiple machines, and to manage different browser versions and browser configurations centrally (instead of in each individual test).
For instance: A hub can be connected to three different nodes, each running a separate browser or different browser version. When tests are run, a request is sent to hub which searches for a node with specified criteria(such as browser version, browser type). Once hub manages to locate it, scripts are sent to the node and tests are run.
What is Docker?
In simple terms, Docker can be termed as a container. Docker users can put things like database, libraries, dependencies into that container, and then use them to create, deploy, and run applications.
By using Docker containers we can set up and pack a software application with all of the contents that are required to build that application, such as databases, libraries, and other dependencies.
- Docker Compose
Docker compose is a part of Docker, but it has it’s own commands and uses
a YAML file to know which images you want to use, how you want to create
those containers and link them together. We define our yml file with
the services we want and then use the docker compose command to create and start all of them at once.
Why use Docker?
The main reason is setting up selenium grid is a time-consuming task. We need to download and run the Selenium server, configure hub and nodes for each machine and run multiple commands. Also, when we set up a normal grid we need to download the Selenium server jar file and run that jar file on each computer in which we are going to set up the Selenium grid. It’s really hard to manage our hub and nodes when the number of instances gets increased.
Additionally, with docker, we can eliminate the need for downloading browser drivers and setting class path because that is also done automatically.
How to start with Docker
Go to https://hub.docker.com/ and search for ‘Selenium hub’. We don’t need to configure it manually, it will be downloaded with ready setup.
Pull the Chrome, Firefox and any other nodes by executing following commands.
docker pull selenium/node-chrome
docker pull selenium/node-firefox
After pulling necessary nodes, if we run “docker images” command, we will see something similar to below.
We can start creating containers as we have images now. There are different ways to run the images and create a Grid with a Hub and Nodes.
1. Docker networking
The Hub and Nodes will be created in the same network, and they will recognize each other by their container name. A Docker network needs to be created as a first step. [1]
$ docker network create grid
$ docker run -d -p 4442-4444:4442-4444 --net grid --name selenium-hub selenium/hub:4.0.0-rc-1-prerelease-20210618
$ docker run -d --net grid -e SE_EVENT_BUS_HOST=selenium-hub \
-e SE_EVENT_BUS_PUBLISH_PORT=4442 \
-e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \
-v /dev/shm:/dev/shm \
selenium/node-chrome:4.0.0-rc-1-prerelease-20210618
$ docker run -d --net grid -e SE_EVENT_BUS_HOST=selenium-hub \
-e SE_EVENT_BUS_PUBLISH_PORT=4442 \
-e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \
-v /dev/shm:/dev/shm \
selenium/node-edge:4.0.0-rc-1-prerelease-20210618
$ docker run -d --net grid -e SE_EVENT_BUS_HOST=selenium-hub \
-e SE_EVENT_BUS_PUBLISH_PORT=4442 \
-e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \
-v /dev/shm:/dev/shm \
selenium/node-firefox:4.0.0-rc-1-prerelease-20210618
When we are done using the Grid, and the containers have exited, the network can be removed with the following command:
# Removes the grid network
$ docker network rm grid
2. Using different machines/VMs
The Hub and Nodes will be created on different machines/VMs, they need to know each other’s IPs to communicate properly. Since this option is not so user-friendly, I will not share details. When required, it can be found in the documentation.
3. Docker Compose
Docker Compose is the simplest way to start a Grid. We just need to create one docker-compose.yaml file in our project folder, and save the code lines from the resource.
Once we look at the file, we could easily recognize that we have one selenium hub image acting as hub, and 3 more images (chrome, firefox and opera) acting as a node.
To execute this docker-compose.yml file, we should use `docker-compose up` command. It will then download images, create the containers, and it will automatically establish the grid environment. We might have more than one yml file — In that case we need to specify the file name as well.
$ docker-compose -f docker-compose-v2.yml up
After running the docker-compose up command, we should see something similar to below.
We can also double-check it from the browser. Mine is currently running on http://localhost:4444/grid/console.
Based on our requirement, we can also increase the number of nodes. Let’s say, I need 2 more chrome drivers to execute my tests. All I need to do is run the following command from the project folder.
$ docker-compose scale chrome=3
When we check our console again, then we could see 2 more Chrome browsers, which are ready to execute test cases.
If we execute “docker ps” command, we could now see 2 more chrome nodes.
Now, we can connect to it through the remoteUrl and run the tests. I just need to update my driver class.
Test Execution
So far so good. Containers are up. Now we have to configure our code and run our tests.
We should keep in mind that Selenium Grid has nothing to do with control or drive the test in parallel manner. The automation test has to be configured in such a way that it is capable of running multiple tests at the same time. Selenium Grid is just a grid of nodes. Each node has a specific platform, browser name, browser version, etc…
If we use TestNG framework, then it is very easy to execute multiple tests parallelly on different browsers with the help of selenium grid. We just need to adjust our testng.xml file. Below, we can see one simple example file, which consists of 4 different classes. We want to execute 2 of them with Chrome browser,1 with Firefox, and 1 with Opera browser parallelly as per the requirement.
Configuring Testng.xml file.
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" ><suite name="SeleniumGridDocker" parallel="classes"> <test thread-count="10" name="Test">
<classes>
<class name="docker.ChromeTest1"/>
<class name="docker.ChromeTest2"/>
<class name="docker.FirefoxTest3"/>
<class name="docker.OperaTest4"/>
<classes>
</test></suite>
If we use Cucumber framework together with JUnit, we should also design our framework in a way that, it allows us to run our tests parallel or cross platform, if that is the requirement of course. Our environment is ready, the rest is completely our needs and requirements. If we don’t need to run paralell or cross-platform, we could also run all tests first with chrome, then Firefox, then Opera as well…
Below example might help us to run our test scenarios with multiple browser at the same time, once we design our project to run parallely. I just added one condition in Hook class, which searches for the respective tags to set the driver.
During the test execution, since we don’t have any browser openning and closing, we can follow our tests either from the command line, or as an alternative we could observe the selenium grid dashboard. Below we can see that, one of the test is executed by the chrome driver.
Once we are done with the execution, we can run the following command to stop containers.
$ docker-compose down
How to invoke Selenium Grid server in Docker Environment automatically
Until now, we have executed everything with a manual human intervention. Wouldn’t it be good to execute without our invention? — Docker containers will be ready, tests cases are executed, and then containers are stopped again.
We just need to create 2 executable files on the root folder, and adjust our project files. Since I am using macOS, I will create 2 .sh folders. (start_dockergrid.sh, stop_dockergrid.sh). Windows users can create .bat executable file.
Inside the folder, we should define the project path and docker command.
We are now one step further — instead of writing commands, we could just click on the green button to run and stop containers.
Next, we need one more class, so that we can execute these commands automatically.
With the help of our “Setup_DockerGrid Class”, our docker_setup method will be executed before all tests, and docker_stop method will be executed after all tests are finished.
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" ><suite name="SeleniumGridDocker" parallel="classes"><test thread-count="10" name="Test">
<classes>
<class name="docker.SetupDockerGrid"/>
<class name="docker.ChromeTest1"/>
<class name="docker.ChromeTest2"/>
<class name="docker.FirefoxTest3"/>
<class name="docker.OperaTest4"/>
<classes>
</test></suite>
In the same way, if we are using Cucumber/JUnit framework, we could also use @BeforeClass and @AfterClass annotations to handle this issue.
If we now run our tests from the command line, or via CI/CD tool, it will be executed without any human intervention. 😎
Cloud Providers
There are companies, like BrowserStack and SauceLabs to name a few, that hold a massive pool of Selenium Grid nodes, allowing you to rent these nodes in order to run cross-browser tests on them. It should be also considered as an alternative option, if it merges also with the company’s or project’s objective. The concept is almost identical to the use of Selenium Grid, with the advantage that you don’t have to manage and maintain the machines, their resources, updates, etc., and of course that it’s much more scalable. For that service, you need to pay of course. ヅ
Summary
When we have numerous test cases, we can use the Selenium grid for speeding up our test case executions. Sometimes we have to run and test our test cases under different operating systems, and at times we have to check our test cases under different browsers as well. We use the Selenium grid under such occasions.
At times configuring the Selenium grid might be a high-cost involving and time-consuming process as we need multiple machines. Under such cases, the best option might be to use the Selenium Grid with Docker. By using this approach, we can save a lot of time and get accurate results in a cost-effective manner.
☕️ Happy testing! ☕️
You can follow me on Medium for more articles, connect with me on LinkedIn