Step 3. 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" ]

FROM mcr.microsoft.com/dotnet/core/sdk:3.1

# set working directory
WORKDIR /app

# Restore
COPY src/src.csproj ./src/
RUN dotnet restore src/src.csproj
COPY test/test.csproj ./test/
RUN dotnet restore test/test.csproj

# Copy src
COPY . .

# Test
CMD [ "dotnet", "test", "test/test.csproj" ]

# 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/...

FROM google/dart

# set working directory
WORKDIR /app

# copy all project
COPY . .

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

# Specify the command from running tests
CMD pub run test

FROM python:3

# set working directory
WORKDIR /usr/src/app

# copy project file
COPY requirements.txt .

# install dependencies
RUN pip install -r requirements.txt

# copy all project
COPY . .

# run test
CMD [ "python", "./test.py" ]

Not available

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.

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 4 to package our microserviceinto a Docker container, before we publish it.

Step 4. Packaging a microservice into a container.