This guide covers using docker-compose
to spin up your application, run E2E tests, and then exit with the results.
This post is a good companion to my post NestJS Integration and E2E Tests with TypeORM, PostGres, and JWT.
The TL;DR is:
docker-compose -f docker-compose.e2e.yml up --abort-on-container-exit --exit-code-from app
For the sake of an example, we’ll be testing a hypothetical API written in NodeJS that uses a Postgres database and a Redis instance. We’ll assume tests are run via jest
. You can substitute any stack!
To begin, ensure that you have recent versions of docker
and docker-compose
installed on your machine.
Dockerfile
Start by creating a Dockerfile in your project folder that specifies how to build an image for your app.
The following example is for a NodeJS web API.
FROM node:14.4-alpine As example
# install build dependencies
RUN apk update && apk upgrade
RUN apk add python3 g++ make
# install packages for sending mail (msmtp = sendmail for alpine)
RUN apk add msmtp
RUN ln -sf /usr/bin/msmtp /usr/sbin/sendmail
# make target directory for assigning permissions
RUN mkdir -p /usr/src/app/node_modules
RUN chown -R node:node /usr/src/app
# use target directory
WORKDIR /usr/src/app
# set user
USER node
# copy package*.json separately to prevent re-running npm install with every code change
COPY --chown=node:node package*.json ./
RUN npm install
# copy the project code (e.g. consider: --only=production)
COPY --chown=node:node . .
# expose port 3500
EXPOSE 3500
docker-compose
Create a docker-compose.e2e.yml
file.
The following example creates a service called app
that runs in the container example
, as specified in the Dockerfile.
The command
property should specify the command that will run your tests inside the container. In our example, this is yarn test:e2e
.
version: '3.8'
services:
app:
container_name: example
build:
context: .
target: example # only build this part of the Dockerfile (see: '... As example' )
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules # 'hack' prevents node_modules/ in the container from being overridden
working_dir: /usr/src/app
command: yarn test:e2e
environment:
PORT: 3500
NODE_ENV: test
DB_HOSTNAME: postgres
DB_PORT: 5432
DB_NAME: example
DB_USERNAME: postgres
DB_PASSWORD: postgres
REDIS_HOSTNAME: redis
REDIS_PORT: 6379
networks:
- webnet
depends_on:
- redis
- postgres
redis:
container_name: redis
image: redis:5
networks:
- webnet
postgres:
container_name: postgres
image: postgres:12
networks:
- webnet
environment:
POSTGRES_DB: example
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
PG_DATA: /var/lib/postgresql/data
volumes:
# - ./seed.db.sql:/docker-entrypoint-initdb.d/db.sql <- run only once when the pgdata volume is first created (when run via docker-compose)
- pgdata:/var/lib/postgresql/data # or specify a local folder like ./docker-volumes/pgdata:/var/lib/postgresql/data
networks:
webnet:
volumes:
pgdata:
logs:
Note how each service shares the same network (called webnet
) so they can communicate with each other.
Tip: you can use .env
files and reference variables from them in a docker-compose.yml
file as follows: ${VARIABLE_NAME}
.
If you wish to specify a particular .env
file in your docker-compose.yml
file:
env_file:
- .env
Run E2E Tests
From your project folder, you can run the following command to run your tests:
docker-compose -f docker-compose.e2e.yml up --abort-on-container-exit --exit-code-from app
The -f
flag specifies a custom configuration file for docker-compose
. If this is not specified, docker-compose
will look for docker-compose.yml
by default.
The up
command tells docker-compose
to bring the containers and services up.
The --abort-on-container-exit
and --exit-code-from
flags are an important combination.
The first flag shuts things down when our test run is complete, and the second flag will use the exit code from the specified service (in our case the one named app
) as the exit code from the overall docker-compose
command.
This is a good setup if you have scripts that run tests, or if you have a continuous integration pipeline that automatically runs tests and requires a pass/fail.
Test runners such as jest will generally exit with code 0
(success) if all tests pass, and exit with a non-zero code (failure) if any tests fail.
package.json
If your project uses npm
, yarn
or similar, you can specify commands to run tests in the scripts
section.
Our docker-compose.e2e.yml
file requires the app
service to run the command
: yarn test:e2e
. For our hypothetical example app, this is specified as follows:
"test:e2e": "NODE_ENV=test jest --config ./test/jest-e2e.json",
Of course, you will need to specify whatever command initiates running E2E tests that’s particular to your project and environment.
To spin up your application via docker-compose
and run tests from your workstation (or CI solution, etc), add the following script:
"test:e2e:docker": "docker-compose -f docker-compose.e2e.yml up --abort-on-container-exit --exit-code-from app"
You can then run via npm run test:e2e:docker
or yarn test:e2e:docker
to spin up your test environment, run your tests, and exit with the results.
If you are using a different package/dependency management solution, you can specify your test-related scripts there. You also have the option to define shell/bash scripts that can run your tests.