/ programming

Dockerize Behave (Selenium) tests

I like to have everything in a Docker containers. Recently we've added Behave
tests to the project where I'm currently working. My assumptions for Docker
container with tests:

  • we should be able to run tests in the background (xvfb)
  • we should be able to run tests using Chrome browser
  • we should be able to use pdb
  • we should be able to run tests on application from other container
    (example: http://localhost:8080 on host)
  • we should be able to use simple commands (shortcuts) instead of long
    docker/docker-compose commands

Let's start with Dockerfile.

FROM python:3.5

MAINTAINER Przemysław Kołodziejczyk <[email protected]>

RUN wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | apt-key add - && \
    echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google.list && \
    apt-get update && \
    apt-get install -y \
        python3-dev \
        python3-pip \
        xvfb \
        google-chrome-stable \
        chromedriver

ENV PATH /usr/lib/chromium/:$PATH

ADD requirements.txt /root/requirements.txt
RUN pip3 install -r /root/requirements.txt

It's a very simple Docker file. We will use Docker image with Python 3.5. During the build process Docker will install Chrome browser and all additional packages. We also need to add an additional path to system PATH to specify where the chromedriver is installed.

Now is the time for docker-compose.yml file.

version: '2'
services:
  behave:
    network_mode: "host"
    shm_size: 256m
    environment:
      - DISPLAY=$DISPLAY
      - CHROME_BINARY_PATH=/usr/bin/google-chrome
      - DBUS_SESSION_BUS_ADDRESS=/dev/null
    volumes:
      - .:/behave
      - /tmp/.X11-unix:/tmp/.X11-unix
    working_dir: /behave

What's going on here. Network mode set to host means that our tests will be able
to access localhost from host so basically it will be possible to run tests for
example on http://localhost:8080. We need shm_size to set size of /dev/shm and
DBUS_SESSION_BUS_ADDRESS set to /dev/null, without that I had a lot of issues
with running Chrome. We need to pass $DISPLAY environment variable and mount
/tmp/.X11-unix to be able to run GUI browser from container. CHROME_BINARY_PATH
is just a path to Chrome browser, I'm using it to set Chrome location in
chromedriver and use Chrome instead of Chromium. (Without that chromedriver
uses Chromium as default option.)

It's mostly everything what is related to the Docker. Below there is only a part
of tests to show how to use CHROME_BINARY_PATH and how to run Chrome. As
you can see the CHROME_BINARY_PATH is set only when it exists in environment.
Not everyone is using Docker and in this way when someone runs tests
without Docker and without that variable, the tests will use browser from user
system (Chromium or Chrome).

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

CHROME_BINARY_PATH = os.environ.get('CHROME_BINARY_PATH')

chrome_options = Options()
if CHROME_BINARY_PATH:
    chrome_options.binary_location = CHROME_BINARY_PATH
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-setuid-sandbox')
browser = webdriver.Chrome(chrome_options=chrome_options)

Here is a small part of docker.sh script to simplify running tests.

#!/usr/bin/env bash

case "$1" in
    background)
        docker-compose run --rm behave behave -D $2 -D xvfb
        ;;
    chrome)
        xhost +si:localuser:root
        docker-compose run --rm behave behave -D $2
        xhost -si:localuser:root
        ;;
    behave)
        xhost +si:localuser:root
        docker-compose run --rm behave behave "${@:2}"
        xhost -si:localuser:root
        ;;
    pdb)
        xhost +si:localuser:root
        docker-compose run --rm --service-ports behave behave --no-capture "${@:2}"
        xhost -si:localuser:root
        ;;
esac

As a $2 parameter we are passing the environment name or URL where the tests
should be run, example: local, staging, http://localhost:8080. Most of that
commands are used to run behave in container. Just remember that if you want
to use pdb to debug tests then you need to add --service-ports parameter.
As you can see there is also a xhost command. This command allows root user
(from Docker container) to access the running X server (from host) and using
this we can display Chrome GUI from container.