To Introduction

Microservice Dockerization

Step 2. Test

Automated tests play a key role in achieving a guaranteed quality of microservices. Testing can be performed on various levels - unit tests for separate classes, end-to-end or integrated tests for microservices with dependencies, and tests that show how microservices work together within the actual system. In this tutorial however, we will only be looking at testing individual microservices, and system tests will not be covered.

To correctly perform testing, especially iterative testing, we need to correctly configure our environment with all of the necessary services and correct configuration parameters. Replicating and supporting testing configurations manually, especially across different operating systems, is far from being easy. Pretty much every development team knows that sometimes tests work perfectly locally, but break on the build server. And finding the cause of this inconsistency is usually quite time consuming.

Dockerizing automated tests provides you with a 100%-repeatable environment that can be replicated on any machine in just a number of minutes. This step of the tutorial contains specific instructions for each of the languages supported by the Pip.Services Toolkit.

Let’s create a separate Docker container for running tests. This container’s build scenario will be defined in a file named Dockerfile.test:

FROM node:8

# set working directory
WORKDIR /app

# Copy project file
COPY package*.json ./

# install ALL node_modules, including 'devDependencies'
RUN npm install

# copy all projectCOPY . .

CMD [ "npm", "test" ]

The scenario for testing is nearly identical to the one we wrote for the build process - the only difference being the last command, which will run the tests in this case.

# Start with the golang v1.13 image
FROM golang:1.13

# set working directory
WORKDIR /app

# Copy project file
COPY go.mod go.sum ./

# Install all go_modules
RUN go mod download

# Copy the entire projectCOPY . .

# Specify the command from running tests
CMD go test -v ./test/...

This scenario uses the standard Go image, version 1.13, as a base image and installs all necessary modules in the /app working directory. Afterwards the rest of the project’s files are copied and the tests are run.

# Start with the Dart image
FROM google/dart:2.7.2

# Set a working directory
WORKDIR /app

# Copy the entire project
COPY . .

# Install all dependencies
RUN pub get
RUN pub get --offline

# Specify the command from running tests
CMD pub run test

This scenario uses the standard Dart image, version 2.7.2, as a base image, sets the working directory to /app, and copies the entire project into this directory. Afterwards, the dependencies are installed from the internet, and the pub get --offline command allows us to cache them. Tests are run as usual using the pub run test command.

Oftentimes, tests may require dependent microservices, databases, message brokers, and other infrastructure services. We can use a docker-compose file to start these services in separate containers and connect them to our test Docker container. We’ll be calling this file docker-compose.test.yml, and the configuration it should contain is listed below:

version: '3.3'

services:
  test:
    build:
      context: ..
      dockerfile: docker/Dockerfile.test
    image: ${IMAGE:-pip/test}
    depends_on:
      - mongo
    environment:
      - MONGO_SERVICE_HOST=mongo
      - MONGO_SERVICE_PORT=27017
      - MONGO_DB=test

  mongo:
    image: mongo:latest

This configuration will start two containers: the first with the application being tested (who’s name is dynamically generated by ${IMAGE:-pip/test}), and the second with a MongoDB (mongo:latest). The test image has a number of environment variables being set to enable proper connectivity to the database from within the Docker Compose environment.

To automate the testing process, create a PowerShell script file named test.ps1 and populate it with the following code:

#!/usr/bin/env pwsh

Set-StrictMode -Version latest
$ErrorActionPreference = "Stop"

# Get component data and set necessary variables
$component = Get-Content -Path "component.json" | ConvertFrom-Json
$testImage="$($component.registry)/$($component.name):$($component.version)-$($component.build)-test"

# Set environment variables
$env:IMAGE = $testImage

try {
   # Workaround to remove dangling images
   docker-compose -f ./docker/docker-compose.test.yml down

   docker-compose -f ./docker/docker-compose.test.yml up --build --abort-on-container-exit --exit-code-from test
} finally {
   # Workaround to remove dangling images
  docker-compose -f ./docker/docker-compose.test.yml down
}

To run the script, simply type:

./test.ps1

When run, this script will perform an automatic build of the test image and run it in the Docker Compose environment we’ve set up. While it runs, the testing process’s progress will be outputted to the console. When finished, the container will automatically be stopped, regardless of the test’s results.

Now that we have automated testing all set up, we can move on to Step 3 to package our microservice into a Docker container, before we publish it.