Running tests in containers with docker-compose

JetThoughts
JTWay
Published in
3 min readJul 3, 2020

--

The main advantages of this way are to have independent environment for the tests running and to reduce the complexity of the test environment setup. Just load and run tests. Consider how to achieve this.

You will see below how you can setup the docker-compose for common Ruby on Rails application. As a bonus, you will be able to reuse those setup on all projects without much changing.

What we want to achieve

Running the tests should be easy. New developers should be able to join the development process without much trouble setting up the test environment. And it can become relatively complex including a set of integration with a bunch of additional services. For example, it requires a running Database.

Test runs should be isolated and repeatable. You don’t want to have that one flaky test that fails only on your machine. A failing test should fail when it’s run by anyone in the team and on CI as well.

Test environment should be as close to the production environment as possible. Having green system integration tests should guarantee that a feature works in production. As your application grows the number of service dependencies might grow as well, and it’s important to verify that integrations with them work correctly and keep the dependencies up to date.

Enter Docker

Utilizing Docker to run tests can help you to solve these problems. All developers will have the same isolated test environment setup, which can be used for running tests on CI as well. New developers won’t need to spend half a day just to set up everything required to run tests.

Tests will be run inside a container, so you’ll need to define one. It’s done in a Dockerfile:

First, define the base image, we use the official CircleCI image here. Then install the required application dependencies and configure ownership and rights for files from mounted volumes (more on that later). In the end, define the tini entrypoint.

tini is a neat tool that helps to reap any zombie processes and forward signals to commands executed in the container.

Wiring up

Running application tasks might require additional services dependencies. In this example, in order to run tests, the app needs an accessible database instance. It can be provided by utilizing the docker-compose.

Here we define two services: app and db. App service is built using the defined Dockerfile, all required environment variables are set here as well.

In the volumes section firstly the application directory is mounted, the rest volumes are added for caching purposes. Here also specified that app service depends on db service.

Please note that we specify the user for the app service and that user should be passed by the caller of the docker-compose command.

It’s done in order to solve the permissions problem, which can occur when processes need to modify files inside the mounted volumes, or when you’ll need to access the files (logs, screenshots, other artifacts) which were generated inside the container. To solve both these problems you need to pass your current user uid to the docker-compose commands.

The db service configuration consists of image name (provided by CircleCI), DB access credentials in environment variables, and volume to store the DB data.

Usage

Docker and docker-compose do a lot of work to make life easier for developers now, but it’s still a lot to remember and type:

export CURRENT_UID=$(id -u):$(id -g)docker-compose up --remove-orphans -d dbdocker-compose build appdocker-compose run app bin/bundle installdocker-compose run app bin/rails db:preparedocker-compose run app bin/rails bin/rails db:schema:loaddocker-compose run app bin/rspec

All these can be extracted into a few utility scripts:

Now setting up and running tests inside a docker container is achieved by only running:

bin/dc-setupbin/dc-test

Docker is a powerful tool to use in your development process. It can make starting and switching between projects fast and easy, and help to ensure that everyone is staying up to date with the technologies being used.

Dmitry Tsvetkov is a Software Engineer at JetThoughts. Follow him on LinkedIn or GitHub.

If you enjoyed this story, we recommend reading our latest tech stories and trending tech stories.

--

--