Sometimes your team members complain about automated tests not working on their computer while you are able to run the automated tests at your end completely fine. We too, at Qxf2, have experienced this issue. Our automated tests run fine at our end but occasionally fail when they run on our colleagues and client machines. Invariably, we trace this sort of failure down to variations in the setup/environment. So we looked to tackle this issue. We decided to build a Docker container for running our Selenium tests. The process was very quick and we decided to help all our fellow testers by sharing how we went about doing it.
This post assumes you already know a little about Docker.
NOTE: If you are just looking for a Docker image, you can skip ‘Step 1. Creating a Dockerfile’ section and directly move on to the step 2 titled as ‘Build/Pull the Docker image’.
Overview:
We will be performing the following steps:
1. Creating a Dockerfile
2. Build/Pull the Docker image
3. Creating a container and running Selenium tests
Step 1. Creating a Dockerfile for running Selenium tests:
To get setup with Docker CE you can refer to this link. To build a Docker image for running our Selenium tests, we needed to perform the following steps:
- Pull a Base image
- Install Xvfb virtual/headless display
- Setup Chrome and Chrome driver
- Setup Firefox and Geckodrivers
- Install Python 2.7 and Python Pip
- Get your project code into image/container and install requirements with help of pip and project requirements.txt file
All the steps except the final one rarely change unless when we decide to change the version of software installed. However, your project code and requirements change frequently. So, we decided to come up with a base image for Selenium which includes the first 5 steps i.e Ubuntu, Chrome with Chrome driver, Firefox with Geckodriver, Xvfb, Python and Python Pip.
We will code these steps into a file called a Dockerfile. A Dockerfile is a text document that contains all the commands a user could execute on the command line to assemble an image. To build the base image for running the Selenium test, we wrote the following Dockerfile.
1. Pull a Base Image
We used Ubuntu 16.04.
#Contents of Dockerfile #Dockerfile to build an image which supports testing our Qxf2 Page Object Model. FROM ubuntu MAINTAINER Qxf2 Services |
2. Install Xvfb virtual/headless display
# Essential tools and xvfb
RUN apt-get update && apt-get install -y \
software-properties-common \
unzip \
curl \
xvfb |
3. Setup Chrome and Chrome driver
# Chrome browser to run the tests RUN curl https://dl-ssl.google.com/linux/linux_signing_key.pub -o /tmp/google.pub \ && cat /tmp/google.pub | apt-key add -; rm /tmp/google.pub \ && echo 'deb http://dl.google.com/linux/chrome/deb/ stable main' > /etc/apt/sources.list.d/google.list \ && mkdir -p /usr/share/desktop-directories \ && apt-get -y update && apt-get install -y google-chrome-stable # Disable the SUID sandbox so that chrome can launch without being in a privileged container RUN dpkg-divert --add --rename --divert /opt/google/chrome/google-chrome.real /opt/google/chrome/google-chrome \ && echo "#!/bin/bash\nexec /opt/google/chrome/google-chrome.real --no-sandbox --disable-setuid-sandbox \"\$@\"" > /opt/google/chrome/google-chrome \ && chmod 755 /opt/google/chrome/google-chrome # Chrome Driver RUN mkdir -p /opt/selenium \ && curl http://chromedriver.storage.googleapis.com/2.45/chromedriver_linux64.zip -o /opt/selenium/chromedriver_linux64.zip \ && cd /opt/selenium; unzip /opt/selenium/chromedriver_linux64.zip; rm -rf chromedriver_linux64.zip; ln -fs /opt/selenium/chromedriver /usr/local/bin/chromedriver; |
4. Setup Firefox and Geckodrivers
# Firefox browser to run the tests RUN apt-get install -y firefox # Gecko Driver ENV GECKODRIVER_VERSION 0.23.0 RUN wget --no-verbose -O /tmp/geckodriver.tar.gz https://github.com/mozilla/geckodriver/releases/download/v$GECKODRIVER_VERSION/geckodriver-v$GECKODRIVER_VERSION-linux64.tar.gz \ && rm -rf /opt/geckodriver \ && tar -C /opt -zxf /tmp/geckodriver.tar.gz \ && rm /tmp/geckodriver.tar.gz \ && mv /opt/geckodriver /opt/geckodriver-$GECKODRIVER_VERSION \ && chmod 755 /opt/geckodriver-$GECKODRIVER_VERSION \ && ln -fs /opt/geckodriver-$GECKODRIVER_VERSION /usr/bin/geckodriver \ && ln -fs /opt/geckodriver-$GECKODRIVER_VERSION /usr/bin/wires |
Note: Please make sure you update the script to download the latest ChromeDriver and geckodriver versions
5. Install Python 2.7 and Python Pip
# python
RUN apt-get update && apt-get install -y \
python \
python-setuptools \
python-pip |
Putting it all together
Our final Dockerfile looks like this:
#Contents of Dockerfile #Dockerfile to build an image which supports testing our Qxf2 Page Object Model. FROM ubuntu MAINTAINER Qxf2 Services # Essential tools and xvfb RUN apt-get update && apt-get install -y \ software-properties-common \ unzip \ curl \ xvfb # Chrome browser to run the tests RUN curl https://dl-ssl.google.com/linux/linux_signing_key.pub -o /tmp/google.pub \ && cat /tmp/google.pub | apt-key add -; rm /tmp/google.pub \ && echo 'deb http://dl.google.com/linux/chrome/deb/ stable main' > /etc/apt/sources.list.d/google.list \ && mkdir -p /usr/share/desktop-directories \ && apt-get -y update && apt-get install -y google-chrome-stable # Disable the SUID sandbox so that chrome can launch without being in a privileged container RUN dpkg-divert --add --rename --divert /opt/google/chrome/google-chrome.real /opt/google/chrome/google-chrome \ && echo "#!/bin/bash\nexec /opt/google/chrome/google-chrome.real --no-sandbox --disable-setuid-sandbox \"\$@\"" > /opt/google/chrome/google-chrome \ && chmod 755 /opt/google/chrome/google-chrome # Chrome Driver RUN mkdir -p /opt/selenium \ && curl http://chromedriver.storage.googleapis.com/2.45/chromedriver_linux64.zip -o /opt/selenium/chromedriver_linux64.zip \ && cd /opt/selenium; unzip /opt/selenium/chromedriver_linux64.zip; rm -rf chromedriver_linux64.zip; ln -fs /opt/selenium/chromedriver /usr/local/bin/chromedriver; # Firefox browser to run the tests RUN apt-get install -y firefox # Gecko Driver ENV GECKODRIVER_VERSION 0.23.0 RUN wget --no-verbose -O /tmp/geckodriver.tar.gz https://github.com/mozilla/geckodriver/releases/download/v$GECKODRIVER_VERSION/geckodriver-v$GECKODRIVER_VERSION-linux64.tar.gz \ && rm -rf /opt/geckodriver \ && tar -C /opt -zxf /tmp/geckodriver.tar.gz \ && rm /tmp/geckodriver.tar.gz \ && mv /opt/geckodriver /opt/geckodriver-$GECKODRIVER_VERSION \ && chmod 755 /opt/geckodriver-$GECKODRIVER_VERSION \ && ln -fs /opt/geckodriver-$GECKODRIVER_VERSION /usr/bin/geckodriver \ && ln -fs /opt/geckodriver-$GECKODRIVER_VERSION /usr/bin/wires # python RUN apt-get update && apt-get install -y \ python \ python-setuptools \ python-pip |
Step 2. Build/Pull the Docker image:
You can either build your own image using above Dockerfile or you can directly download the image from our Docker Hub repository.
To build the image using above dockerfile, you need to save dockerfile to any directory in your system and use the following command:
docker build -t image_name path/to/dockerfile |
To directly download it from our Docker Hub repository, you need to use the following command:
docker pull qxf2rohand/qxf2_pom_essentials |
Step 3. Creating a container and running Selenium tests:
We decided to use this image for testing our Qxf2’s open sourced GUI automation framework. We named the image as qxf2_pom_essentials. qxf2_pom_essentials image is capable of running any Python based Selenium tests. To run the Selenium tests using this image, you need to go through following steps:
- Create a Docker container out of this image and enter into the container using the following command:
docker run -it qxf2rohand/qxf2_pom_essentials "/bin/bash"
- Export display and enable Xvfb using following 2 commands:
export DISPLAY=:20 Xvfb :20 -screen 0 1366x768x16 &
- Install Selenium using pip
pip install selenium
- Use any Linux editor you like and add your test inside the container. You can also use the sample Selenium code (selenium_docker.py) given below. This selenium test visits Qxf2 Services website and prints the title.
# contents of selenium_docker.py from selenium import webdriver driver = webdriver.Firefox() driver.get("http://www.qxf2.com") print driver.title driver.quit()
- Run the selenium test using the following command:
python selenium_docker.py
The output will be similar to the screenshot shown below.
Note: The above steps are only to show you how to use our Docker image. At Qxf2 Services, for testing our Qxf2’s Public Page Object Model we use another Dockerfile which automatically gets our code from GitHub, setup our requirements, run shell script file to enable Xvfb and run the test.
To know more about how to get your code inside the container and run the test, please stay tuned. We will post about it soon. Until then enjoy running your tests anywhere without thinking about environment set up using this Docker image. Happy testing!
If you are a startup finding it hard to hire technical QA engineers, learn more about Qxf2 Services.
I love technology and learning new things. I explore both hardware and software. I am passionate about robotics and embedded systems which motivate me to develop my software and hardware skills. I have good knowledge of Python, Selenium, Arduino, C and hardware design. I have developed several robots and participated in robotics competitions. I am constantly exploring new test ideas and test tools for software and hardware. At Qxf2, I am working on developing hardware tools for automated tests ala Tapster. Incidentally, I created Qxf2’s first robot. Besides testing, I like playing cricket, badminton and developing embedded gadget for fun.
Hi Rohan,
Thanks for this page, which is very useful to me.
One more question I have is would the Dockerfile copy you provided, may also create an env for runing “py.test” cases ?
Thanks
John , Yes it can create env for running py.test cases provided that you have included step in dockerfile to install pytest plugin . Hope that helps you
Hi Rohan,
I just realize where the problem is, here is the details.
1) I was using exactly the Dockerfile provided on this page, The image was created “successfully”, while the chromdriver part was actually failing, here is the log says:
”
Removing intermediate container 174dbbcb223b
—> 594b60de714d
Step 6/10 : RUN mkdir -p /opt/selenium && curl http://chromedriver.storage.googleapis.com/2.30/chromedriver_linux64.zip -o /opt/selenium/chromedriver_linux64.zip && cd /opt/selenium; unzip /opt/selenium/chromedriver_linux64.zip; rm -rf chromedriver_linux64.zip; ln -fs /opt/selenium/chromedriver /usr/local/bin/chromedriver;
—> Running in ebf3ee3157a4
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 –:–:– –:–:– –:–:– 0
curl: (52) Empty reply from server
unzip: cannot find or open /opt/selenium/chromedriver_linux64.zip, /opt/selenium/chromedriver_linux64.zip.zip or /opt/selenium/chromedriver_linux64.zip.ZIP.
:.”
2) Once the image was created, I was able to have it stared, and I was able to have the chromedriver installed on top of it.
3) When I was trying your “python selenium_docker.py”, which is for firefox, I was able to get the exact result. but if I was to switch it to Chrome, I got this kind of message:
”
Traceback (most recent call last):
File “tests/selenium_docker.py”, line 4, in
driver = webdriver.Chrome()
File “/usr/local/lib/python2.7/dist-packages/selenium/webdriver/chrome/webdriver.py”, line 68, in __init__
self.service.start()
File “/usr/local/lib/python2.7/dist-packages/selenium/webdriver/common/service.py”, line 98, in start
self.assert_process_still_running()
File “/usr/local/lib/python2.7/dist-packages/selenium/webdriver/common/service.py”, line 111, in assert_process_still_running
% (self.path, return_code)
selenium.common.exceptions.WebDriverException: Message: Service chromedriver unexpectedly exited. Status code was: 127
”
So my question is, if manually installing chromedriver on top of the created image, is a valid way to do for Chrome case ?
Thanks,
John
Hii John , Actually chromedriver should have installed automatically while creating image . Now since you have manually installed chromedriver on top of image , can you check if the chrome driver version which you installed is compatible with chrome browser version . What version you are using ?
I followed the steps above to build an image. I got this error when running the sample python test –
driver = webdriver.Firefox()
File “/usr/local/lib/python2.7/dist-packages/selenium/webdriver/firefox/webdriver.py”, line 174, in __init__
keep_alive=True)
File “/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/webdriver.py”, line 157, in __init__
self.start_session(capabilities, browser_profile)
File “/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/webdriver.py”, line 252, in start_session
response = self.execute(Command.NEW_SESSION, parameters)
File “/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/webdriver.py”, line 321, in execute
self.error_handler.check_response(response)
File “/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/errorhandler.py”, line 242, in check_response
raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.WebDriverException: Message: newSession
The tests runs fine on image qxf2rohand/qxf2_pom_essentials from repo. What’s difference between the two?
Hi,
Can you please check the Firefox version and driver version?.
Hi Rohan,
I want to include a step in Dockerfile that should run some pip install commands.
How can I include requirements.txt in the Dockerfile in the example mentioned.
Hi Saurabh,
You may refer – https://jpetazzo.github.io/2013/12/01/docker-python-pip-requirements/, that will give you some idea about how you can solve the problem. If still, you face the issue then let us know what you have tried and problems you encountered
Hi,
I already have a main.py created but how would I add the contents to the container?
When I enter the root@ terminal, python can’t open ‘main.py’: [Errno 2] No such file or directory
Kat,
Where did you create the main.py file?
i) After you run the (1) command in ‘Creating a container and running Selenium tests’ section, you should enter into a container,
ii) Inside the container run (2) and (3) commands,
iii) Inside the container, create a test file ‘main.py’ with the below contents
“`
from selenium import webdriver
driver = webdriver.Firefox()
driver.get(“http://www.qxf2.com”)
print driver.title
driver.quit()
“`
iv) Run the test.
Hope this helps!
Hi,
I have followed all your steps, I’m unable to launch Google-chrome from the docker
Error:
Traceback (most recent call last):
File “testselenium.py”, line 3, in
browser = webdriver.Chrome()
File “/usr/local/lib/python3.8/site-packages/selenium/webdriver/chrome/webdriver.py”, line 76, in __init__
RemoteWebDriver.__init__(
File “/usr/local/lib/python3.8/site-packages/selenium/webdriver/remote/webdriver.py”, line 157, in __init__
self.start_session(capabilities, browser_profile)
File “/usr/local/lib/python3.8/site-packages/selenium/webdriver/remote/webdriver.py”, line 252, in start_session
response = self.execute(Command.NEW_SESSION, parameters)
File “/usr/local/lib/python3.8/site-packages/selenium/webdriver/remote/webdriver.py”, line 321, in execute
self.error_handler.check_response(response)
File “/usr/local/lib/python3.8/site-packages/selenium/webdriver/remote/errorhandler.py”, line 242, in check_response
raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.WebDriverException: Message: unknown error: Chrome failed to start: exited abnormally.
(unknown error: DevToolsActivePort file doesn’t exist)
(The process started from chrome location /usr/bin/google-chrome is no longer running, so ChromeDriver is assuming that Chrome has crashed.)
Shivakumar
This issue might be because of the mismatch between your chrome driver and the chrome browser. Please check the version of the chrome driver you are using and is it compatible with the chrome browser version you have. Refer here for more details
Hope this helps !
Thanks for your replay.
I have checked the Chrome and Chromedriver versions but both are in same versions
root@ed04a2e7d150:~# google-chrome-stable –version
Google Chrome 80.0.3987.122
root@ed04a2e7d150:~# chromedriver –version
ChromeDriver 80.0.3987.106 (f68069574609230cf9b635cd784cfb1bf81bb53a-refs/branch-heads/3987@{#882})
root@ed04a2e7d150:~#
I have tried to launch the google chrome alone from the container, But it was not launching and thrown the below error.
root@ed04a2e7d150:~# google-chrome-stable –no-sandbox
[13627:13627:0227/062121.563926:ERROR:browser_main_loop.cc(1512)] Unable to open X display.
root@ed04a2e7d150:~# [0227/062121.574513:ERROR:nacl_helper_linux.cc(308)] NaCl helper process running without a sandbox!
Most likely you need to configure your SUID sandbox correctly
I have cross-checked the xvfb, it already installs in the docker container
root@ed04a2e7d150:~# apt-get install -y xvfb
Reading package lists… Done
Building dependency tree
Reading state information… Done
xvfb is already the newest version (2:1.20.4-1).
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
@Shivakumar Have you executed below 2 commands before executing the script
export DISPLAY=:20
Xvfb :20 -screen 0 1366x768x16 &
@Roham
I have executed the corresponding command as well but it’s not working and showing the same error link
Hi, can you try running without using the root user?
Hi Shiva is this issue resolved to you i am getting the same error and some times protocol error , could you please help
Hi,
Can you please share more details about the `chrome options` you have used and the full error message.
Thanks
Hi.. thanks for the post .. is it possible to take a screenshot while running the selenium tests inside the docker image you have provided ..
Hello,
Please refer the blog
https://qxf2.com/blog/how-screenshots-are-saved-in-qxf2-framework/
getting protocol error
Hi,
Can you please share more details about this protocol error.
Thanks