Test Drive Your Dockerfiles with RSpec and ServerSpec

Mason Fischer

Docker, a portable, lightweight runtime and packaging tool, is all the rage these days. It’s hard to go to any sort of tech meetup without overhearing people gushing to each other about how cool it is. I’d written some Dockerfiles but wanted to start test driving them. It’s actually fairly straightforward using RSpec and ServerSpec. Here’s how to TDD a Dockerfile that installs Node on Ubuntu.

Start by writing a failing test. This tests that we have Ubuntu 14 installed:

# spec/Dockerfile_spec.rb

require "serverspec"
require "docker"

describe "Dockerfile" do
  before(:all) do
    image = Docker::Image.build_from_dir('.')

    set :os, family: :debian
    set :backend, :docker
    set :docker_image, image.id
  end

  it "installs the right version of Ubuntu" do
    expect(os_version).to include("Ubuntu 14")
  end

  def os_version
    command("lsb_release -a").stdout
  end
end

First we build the Docker image from the Dockerfile in the current directory. Then we use set, part of the ServerSpec DSL, for telling it how to connect to the host we are testing. We then test that the results of the command lsb_release -a includes “Ubuntu 14”. I’ve pulled it out into its own method for clarity.

Now let’s run our test and watch it fail:

$ rspec spec/Dockerfile_spec.rb
...
Cannot build a directory without a Dockerfile (Docker::Error::ServerError)
...

Add a Dockerfile:

FROM ubuntu:14.04
MAINTAINER Mason Fischer <mason@thoughtbot.com>

And watch it pass:

$ rspec spec/Dockerfile_spec.rb

1 example, 0 failures

Testing package installation

We can also test that packages are installed. For example, let’s add a test that Node is installed:

# spec/Dockerfile_spec.rb

require "serverspec"
require "docker"

describe "Dockerfile" do
  before(:all) do
    image = Docker::Image.build_from_dir('.')

    set :os, family: :debian
    set :backend, :docker
    set :docker_image, image.id
  end

  it "installs the right version of Ubuntu" do
    expect(os_version).to include("Ubuntu 14")
  end

  # This test is new
  it "installs required packages" do
    expect(package("nodejs")).to be_installed
  end

  def os_version
    command("lsb_release -a").stdout
  end
end

Again we can watch our test fail:

$ rspec spec/Dockerfile_spec.rb
...
  1) Dockerfile installs required packages
     Failure/Error: expect(package("nodejs")).to be_installed
       expected Package "nodejs" to be installed
...

Let’s add the following line to the end of our Dockerfile:

RUN apt-get update && apt-get install -y nodejs

And watch the test pass.

$ rspec spec/Dockerfile_spec.rb

2 examples, 0 failures

ServerSpec can test more than just packages. You can test commands, ports, services and pretty much anything you can dream up.

Thanks to Taichi Nakashima and Pieter Joost Van de Sande for their posts on TDDing Dockerfiles which is where I got a lot of this information.

The example Dockerfile and Dockerfile_spec.rb are posted in this Gist.