Service tests with Docker
By: Konstantinos Konstantinidis
Tags: Net Core, Continuous Integration, Developer Productivity, Docker, Testing

In this blog post, we will investigate service tests and discuss why they are useful. We’ll explore a sample solution and go through the tooling needed to run them efficiently. Additionally, we’ll see why this improves the overall code quality, velocity and delivery pipeline of a software team.

Docker

Docker is a virtualization platform that allows us to run containers in memory. We will be using its capabilities to spin up dependencies such as databases and others locally.

Definition and usefulness

Testing takes place at various levels and one of those is service tests. Sometimes called integration tests or service-level acceptance tests, they usually take place after unit tests. Service tests ensure that business requirements are fulfilled within its bounded context. A bounded context is a slice of the business domain. A very crud bounded context would be considered the area of Subscriptions in Netflix.

Unit tests exercise a given method or smallest piece of code while service tests exercise a given logical service end to end, treating it as a black box and expecting it to fulfil the specified acceptance criteria. When investing in this way of testing, you get proof that everything works and test reports that can prove that you have test coverage for a given area. This is especially useful when auditing and regulatory requirements are high when it comes to code quality.

Development teams should create service tests when delivering features. They should be a criterion for code completeness. Code that is typically covered by both unit tests and service tests minimizes the chances for bugs. You should have fewer service tests than unit tests.

Test Pyramid

Historically it has been quite difficult to allow developers to run service tests without first having many dependencies locally installed, at the very least a database. Nowadays, you may also depend on cloud resources such as Azure Storage. This increases the resources needed to run them and introduces a requirement for an internet connection. Ever tried to coding while on an airplane? Similarly, the CI requirements increase if you want to run them there.

Fortunately, with the advent of Docker and containers, it’s now possible to get away from all that.

Expenses App

For this blog post, a sample repository has been created. Certain areas of the code have been implemented simplistically on purpose.

The use case included in the code describes of a businessman trying to submit his business expense along with a receipt and then trying to retrieve it. For this demo, we have introduced a dependency on Postgres and Azure Blob Storage.

Coding features in a human-readable way

Leveraging tooling that allows you to write the business requirements in a way that maximizes readability for your stakeholders is recommended. Cucumber’s Gherkin language is an immensely popular approach. In the code examples of this post, we use SpecFlow which supports Gherkin out of the box.

When using Gherkin, you can specify your features in separate files. Each feature includes a set of scenarios. Scenarios are broken into steps that you need to implement in Step files.

Here’s the gherkin file that describes the feature and a simple scenario.

Feature file for the submit expense feature

The docker side

Docker allows us to create docker-compose files which describe how to bring up the entire service and its dependencies locally without anything other than Docker Desktop installed. Here’s the docker-compose file of this example.