Docker unit test: how to test a Dockerfile (Guide 2020)
Tags: docker, devops, tutorial, codequality
Published: 2020-10-07 13:50:38


You know you should *test everything*...

**Don't you?**

Well, writing unit test for Docker should be part of your daily routine while developing a new Dockerfile. It can save you a loooot of time spent running a Docker image trying to figure out why is not working and it will drastically reduce your fear of rebuilding and updating a container (If you still don't believe me on testing, read this article by [James Shore](http://www.jamesshore.com/v2/books/aoad1/test_driven_development)).

**In this guide you will learn:** which tools can help you testing your Dockerfile, how to write a unit test for Docker and how to automate it in a continuous integration pipeline.

## Docker container structure

The best tool I can raccomand to write a unit test for a Docker is the [Container Structure Test](https://github.com/GoogleContainerTools/container-structure-test) framework.
This framework, developed by Google, makes super easy to test the structure of your container image.

### How to install

If you are using Linux run:

```bash
curl -LO https://storage.googleapis.com/container-structure-test/latest/container-structure-test-linux-amd64 && chmod +x container-structure-test-linux-amd64 && sudo mv container-structure-test-linux-amd64 /usr/local/bin/container-structure-test
```

### Test options

Container Structure Test offers 4 types of test:

- *Command Tests*: execute a command in your image and check the output
- *File Existence Tests*: check if a file is, or isn't, present in the image
- *File Content Tests*: check the content of a file
- *Metadata Test*: check if a container metadata is correct

## How to write a docker unit test

All you need is a Dockerfile and a `.yaml` or `.json` file that contains your test cases.

### Write your first Docker unit test

For this example we will use the following Dockerfile for an image that can be used in the CI to build the code using [Bazel](https://bazel.build/).

```bash
FROM ubuntu:bionic

RUN apt-get update \
  && apt-get install -y curl gnupg \
  && curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor > bazel.gpg \
  && mv bazel.gpg /etc/apt/trusted.gpg.d/ \
  && echo "deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8" > /etc/apt/sources.list.d/bazel.list \
  && apt-get update \
  && apt-get install -y bazel \
  && rm -rf /var/lib/apt/lists/*

RUN groupadd -g 1000 user \
  && useradd -d /home/user -m -u 1000 -g 1000 user \
  && chown -R user:user /home/user \
  && mkdir -p /bazel/cache \
  && chown -R user:user /bazel

RUN echo "build --repository_cache=/bazel/cache">/home/user/.bazelrc
```

And can be built with:

```bash
docker build -t docker-unit-test .
```

Now we have a Docker image that we set up as root but on the CI we want to mimic the developer build environment as much as possible, to do this we will run the build as a non root user.

*What could go wrong?*

**A lot of things actually!**

Do the user own build configuration files? Or the cache folder? Well you can check all of that before deploying your Docker image anywhere.

Let's create `unit-test.yaml` to test it!

```bash
schemaVersion: '2.0.0'
fileExistenceTests:
  - name: 'Check bazel cache folder'
    path: '/bazel/cache'
    shouldExist: true
    uid: 1000
    gid: 1000
    isExecutableBy: 'group'
fileContentTests:
  - name: 'Cache folder config'
    path: '/home/user/.bazelrc'
    expectedContents: ['.*build --repository_cache=/bazel/cache.*']
```

The first test `Check bazel cache folder` will check that the cache folder exists and is owned by the non-root user. The second test `Cache folder config` will check that the Bazel build configuration file content is as expected.

Everything is set, we can run our test in this way:

```bash
$ container-structure-test test --image docker-unit-test --config unit-test.yaml

=======================================
====== Test file: unit-test.yaml ======
=======================================
=== RUN: File Content Test: cache folder config
--- PASS
duration: 0s
=== RUN: File Existence Test: Check bazel cache folder
--- PASS
duration: 0s

=======================================
=============== RESULTS ===============
=======================================
Passes:      2
Failures:    0
Duration:    0s
Total tests: 2

PASS
```

This framework can be super useful for testing your Docker image before shipping it, it's fast and easy to use.

## Automate the testing of Docker containers

Ok now we have our Dockerfile and tests ready, it's time to automate the testing process!

In this example I'm assuming that you have an [Ansible](https://www.ansible.com/) pipeline that you use in Continuos Integration to build, tag and push a docker image. We are going to create a new task for that pipeline to execute the Docker unit test.

```bash
- name: unit test Docker Image
  shell: |
    container-structure-test test --image {{ docker_image }} --config {{ test_file }}
    if $?
    then
      echo "Test Failed"
      exit 1
    else
      echo "Test Succeeded"
      exit 0
    fi
```

## This is it!

Reach me on Twitter [@gasparevitta](https://twitter.com/gasparevitta) and **let me know your thoughts!**

I hope you find it useful and that you will start testing your Dockerfile from now on.

You can find the code snippets on [Github](https://github.com/gasparev/blog-code-snippets/tree/master/docker-unit-test)

*This article was originally published on [my blog](https://www.gasparevitta.com/posts/docker-unit-test-dockerfile-image/). Head over there if you like this post and want to read others like it!*