In this article I want to discuss about a subject I have researched recently: creating test cases in a distributed event-driven / microservices environment.
I will discuss the following:
how to use docker containers to create a local environment to test dependent services that have not been implemented yet but have a contract definition ready to be used.
how to run unit and integration test cases against the "mock" services running inside the containers.
how to use testing frameworks as Spring Cloud and PACT in a consumer-driven TTD approach.
how to write test cases against broker systems integrated in a microservices, event-driven environment.
The topics addressed will be divided in a series of articles, so let's start with the first one: contract testing using docker containers.
First, why do we need contact testing?
In a distributed microservices environment with tens of services interacting with each other, it make sense to start building the services - and create test cases incorporating the dependent services - at the time when these dependent services are not built yet but we know how their APIs looks like.
Right at the time when the architecture team has completed defining the API's schema for a service by providing a Swagger file, this schema - that is a contract the service will have to respect at runtime - can be used to create a "mock" service that will run in a local "dockerized" environment where test cases can be written and executed against this service.
Why it "make sense" to start building the services in this way? The quick answer is: because it defines a process that has a series of benefits hence becoming one of the "best practices" a development team needs to follow.
In software development, it is better to include the testing from the beginning phase of the development process:
it is better to involve the QA early into the process to get familiarized with the requirements and create the test cases,
it is better to define a process that can benefit from running test cases that are created based on architecture specifications - contracts - and verifies them constantly during the local development or during the execution of the promotion pipeline.
At the same time, the test cases can be done in isolation, without having to spawn the whole framework: one service can test its dependency on the immediate contract and only on this one.
Another advantage is the fact that an end-to-end integration test case it is most likely easier to get executed and passed when all the individual integration test cases have been performed successfully for a while.
One last advantage I want to mention here is: its simplicity. If we can setup an environment where we don't have to write any line of code that will give us the ability to create these test cases, wouldn't be this a great benefit?
Let's get into the details:
Before we start creating a service, we have defined a Swagger file containing the APIs definitions, the input and output parameters with their schemas, the return codes returned by each function. This Swagger file can be exported easily as a JSON file as well.
We will be using this file to generate a mock service for the dependent service we need to test. This approach it is used in all 3 cases I am presenting in this article: using docker containers or using a testing framework as Spring Cloud or PACT.
In the simple example I have created, a Postal Office service depends on a Telegram service to send a telegram:
first, the Telegram service creates the telegram interacting with a MongoDB database then
the Postal Office calls into its Payment Service to execute a payment then
a message it is returned indicating the telegram has been sent successfully.
This is the structure of my project:
The swagger.json file is the sinny-telegram's swagger file used as a contract.
The docker-compose.yml file contains the images required to build the mock service out of this swagger.json file and builds and image of the Postal Office service as well.
The entries from the docker-compose.yml file that allows for the mock service to gets created can be found in the article from below:
By executing the command: docker-compose up, several containers are loaded as you can see below:
An integration test case it is executed against the telegram mock service (openapi_mock container) and validates that a telegram has been created successfully.
mvn clean install test (the pom.xml file has been configured using maven.surefire plugin)
Other component test cases can be executed, and have been written using JUnit, Mockito, MockMVC:
Simple and powerful process!
Comments