Docker for Testing with Capybara

Chris Emerson
5 min readMar 26, 2018

--

A practical introduction

Photo by Ferdinand Stöhr on Unsplash

As QA we don’t just want to write tests: we want to be able to use the tests we write. While it’s straightforward to run tests on our own computers, we may want to run tests on a schedule, or as part of continuous integration. To do this we need a portable solution.

Thankfully there’s a (relatively) straightforward way to solve these problems: Docker.

Introducing Docker

Docker allows us to ‘containerize’ code. This means that a program and everything needed to run it is bundled together into a standalone, executable* unit — what Docker calls a container. This container can then be run anywhere Docker is supported with little to no additional setup.

While containers themselves are not typically moved across environments — think of them as a running program — Docker creates things called ‘images’ that are effectively environment-independent instructions on how to build and run a container. Images are what we will use to move our code around.

Once our tests run in Docker we no longer need to worry about setting up a whole testing environment each place we want to run our tests. This makes it very easy to run test code with continuous integration tools like Jenkins, allowing you to have tests run automatically when code is changed on a project. If you can execute scripts on a machine, VM, or other systems that support Docker, you can run your tests.

We can use Docker to avoid dependencies on browser executables as well. There are official Docker images for many versions of Firefox and Chrome Selenium servers, and these images are reliably updated by the Selenium community.

For the rest of this guide, we’ll focus on running our tests on Selenium servers running in Docker.

Hands-on practice

A Running Start

Installing Docker is straightforward and well covered in the official documentation so we won’t cover it here.

Once you install Docker, go to the command line and run “docker run hello-world”. This does a couple things: it checks if the “hello-world” image is stored locally, downloads the image from Docker Hub if it isn’t, starts up a container from the image, then executes the commands associated with the image and shuts the container down. More details on these steps can be found in the output of the image itself, as well as the first part of the official Docker walkthrough.

This is Docker at its most basic: if you want to run some code, and that code is in an image you can access, you can just say “docker run” (with any necessary parameters) and it will work.

Of course, we don’t really care about running a ‘Hello World’ program — we care about our tests! Thankfully starting our own containerized Selenium Server is nearly as simple — just run docker run --name selenium_firefox -p 4444:4444 selenium/standalone-firefox. This downloads the latest version of the official Firefox Selenium Server image (if necessary) and then runs it.

A quick explanation of the options we used: The -p 4444:44444 argument maps your computer’s port 4444 (the first number) to port 4444 inside the container (the second number). While Selenium Server listens to port 4444 inside the container and you, therefore, shouldn’t change that number, you can use any (non-reserved) port number you like for the first. The --name option and associated selenium_firefox argument are purely optional, but they provide the container a consistent and descriptive name, which makes it easier to refer to the container as well as tell it apart when you have many containers running.

Assuming you use port 4444, you can see the selenium server console running in your browser by going to localhost:4444//wd/hub. When you’re done with the container you can stop it via docker container stop selenium_firefox if you used the --name option we suggested, or you can use docker container ls to view all running containers and then call docker container stop followed by the name or container ID of your container (partial container IDs work fine here as long as you provide enough of the ID that it is unique).

Before we move on, it’s worth noting that the Firefox server above will work regardless of whether you have GeckoDriver (the Selenium driver for Firefox) or Firefox installed on your computer at all — the image contains instructions for everything it needs to run, and doesn’t depend on anything you’ve installed yourself (expect Docker, of course).

So now we have our own Selenium Firefox server on demand. Let’s run tests on it!

Serving Up Tests

Let’s write a simple test to run on our server. We use the Capybara framework for Ruby here, but the overall approach applies to Selenium-backed frameworks in general.

Create a new folder for your code. Call it something like selenium_remote_test. Add a new file called Gemfile (no file extension) and put the text gem selenium-webdriver in it. This Gemfile defines the dependencies of our Ruby code (this is like package.json for those more familiar with Javascript).

If you haven’t already, install Bundler to help install our dependencies, then run bundle install in this folder in your terminal.

Next paste the following into a new file, test.rb:

At this point you should have at least two files: test.rb, containing your test code, and Gemfile containing your dependencies. You may also have a generated Gemfile.lock file that bundler creates to inform Ruby of your project’s dependencies in detail.

Now you can run this against your server (make sure it’s running first!) via ruby ./test.rb.

And there you have it! You’ve successfully run tests against a Selenium server running in Docker.

Let’s recap what we’ve accomplished so far: We’ve gone over what Docker is and why as QA we might want to use it. We then installed Docker and ran our first container, hello-world. Finally, we ran a Selenium server using Docker and ran a basic test against it.

There is much more to learn about Docker, but this is a good start! From here you can start to learn about running Selenium Grid using Docker-Compose, putting the tests themselves into containers, and many other improvements Docker can bring to your process.

Happy testing!

* Containers are not executables, strictly speaking, but it’s a useful way to think about them for our purposes

--

--