Testing Your Rails Application with Docker

Written by: Marko Locher

4 min read

In my last article, I showed you how to move a Rails development environment to Docker (and Docker Compose). This time, I want to expand on that and discuss some improvements regarding testing your Rails application.

Running your basic test suite is done quite easily. With the configuration from my last post, you can simply run the following commands to spin up the environment, create and seed the database, and run your test suite.

docker-compose up
docker-compose run -e "RAILS_ENV=test" app rake db:create db:migrate
docker-compose run -e "RAILS_ENV=test" app rake test

This is basically the same way you would run your tests locally and though it might speed up test times a bit (depending on the hardware Docker is running on), it isn't a huge improvement.

But you could run the tests in multiple containers at the same time.

Running Individual Tests

As with the app running locally, you can run individual test suites by specifying the filename in the run command.

docker-compose run -e "RAILS_ENV=test" app rspec spec/path/to/spec.rb

Running Tests in Parallel Containers

If your Docker host is beefy enough, you could also run your tests in parallel using multiple containers. There are tools like parallel_tests available, which allow you to run your tests in parallel on a single machine; you might want to reuse those as well.

Browser Testing

If you want to run tests via Capybara or a similar tool, you can simply extend your Dockerfile to include the necessary browsers as well as the xfvb package, which provides a virtual display server.

...
# The Firefox package is called Iceweasel on Debian, but still provides the
# `firefox` binary.
RUN \
  DEBIAN_FRONTEND=noninteractive \
  apt-get install -y \
    chromium \
    iceweasel \
    xvfb
...

Include the webdriver in your Gemfile, and you're good to go. I'd also recommend taking a look at the headless gem, which provides an easy to use wrapper around xvfb and allows you to capture images or video as well.

Alternatively you could switch to using PhantomJS, which provides a headless version of WebKit and doesn't require any additional packages. Install it by adding the following commands to your Dockerfile.

...
ENV PHANTOMJS_VERSION=1.9.8
RUN \
  cd /usr/local/share && \
  wget https://phantomjs.googlecode.com/files/phantomjs-${PHANTOMJS_VERSION}-linux-i686.tar.bz2 && \
  tar xvf phantomjs-${PHANTOMJS_VERSION}-linux-i686.tar.bz2 && \
  rm phantomjs-${PHANTOMJS_VERSION}-linux-i686.tar.bz2 && \
  ln -s /usr/local/share/phantomjs-${PHANTOMJS_VERSION}-linux-i686/bin/phantomjs /usr/local/bin/phantomjs

Note: We're using version 1.9.8 of PhantomJS because Linux binaries for version 2 aren't yet available. If you want to use version 2, you'd need to compile it yourself, but you could then use the above command to download and install it.

Running Development/Test Only Dependencies

If you require dependencies, which should only be running in your test development or environment, you can quite easily add a new container definition in the docker-compose.yml and adapt your configuration accordingly.

For example, Discourse uses Mailcatcher during development. In a traditional setup, you'd be required to install the software on your development computer. However, it becomes even easier with the containerized setup.

Adapt your application definition as detailed below (the development.rb configuration already includes the configuration to send mails via the port defined by Mailcatcher, so you don't need to change it at all).

app:
  ...
  links:
    ...
    - mailcatcher
...
mailcatcher:
  image: schickling/mailcatcher
  ports:
    - "1080:1080"

Once you restart the environment, you can open the Mailcatcher web interface on the IP of your Docker host and see any mail sent via your application.

Docker on Codeship

Like I mentioned in my last post, Codeship is currently preparing a Docker-based CI infrastructure as well. We'll use Docker Compose (with some extensions) to define your test environment and then have a separate YAML-based configuration for configuring which steps to run.

The platform is still under development, so the available options aren't set in stone, but the following configuration would run various tests on multiple containers for the Discourse repository.

# codeship-services.yml
app:
  build:
    image: codeship/discourse
    dockerfile_path: Dockerfile
  environment:
    RAILS_ENV: test
  links:
    - postgresql
    - redis
  volumes_from:
    - data
postgresql:
  image: postgres:9.4
redis:
  image: redis:3.0
data:
  image: busybox
  volumes:
    - /data

Combined with the following step definition, we will run your tests in three Docker containers in parallel.

# codeship-steps.yml
- type: parallel
  service: app
  steps:
    - command: bundle exec rspec
    - command: bundle exec rake plugin:spec
    - command: bundle exec rake qunit:test

Head over to Codeship Docker Beta to sign up for the beta.

PS: If you liked this article you might also be interested in one of our free eBooks from our Codeship Resources Library. Download it here: Continuous Integration and Continuous Delivery with Docker

Stay up to date

We'll never share your email address and you can opt out at any time, we promise.