Preparing a Docker image for running Selenium tests

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:

  1. Pull a Base image
  2. Install Xvfb virtual/headless display
  3. Setup Chrome and Chrome driver
  4. Setup Firefox and Geckodrivers
  5. Install Python 2.7 and Python Pip
  6. 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:

  1. 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"
  2. Export display and enable Xvfb using following 2 commands:
    export DISPLAY=:20
    Xvfb :20 -screen 0 1366x768x16 &
  3. Install Selenium using pip
    pip install selenium
  4. 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()
  5. Run the selenium test using the following command:
    python selenium_docker.py

    The output will be similar to the screenshot shown below.

    Fig. 1 Output of selenium_docker.py

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.

Subscribe to our weekly Newsletter


View a sample



22 thoughts on “Preparing a Docker image for running Selenium tests

  1. 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

  2. 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

    1. 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 ?

  3. 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?

  4. 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.

  5. 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

    1. 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!

  6. 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.)

    1. 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 !

      1. 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.

      2. @Roham
        I have executed the corresponding command as well but it’s not working and showing the same error link

    2. Hi Shiva is this issue resolved to you i am getting the same error and some times protocol error , could you please help

      1. Hi,
        Can you please share more details about the `chrome options` you have used and the full error message.

        Thanks

  7. Hi.. thanks for the post .. is it possible to take a screenshot while running the selenium tests inside the docker image you have provided ..

Leave a Reply

Your email address will not be published. Required fields are marked *