In this article, you’ll see a simple and scalable way of testing the integration of microservices that enables development speed. To get the most of this article you should be familiar with Jenkins, Drone, Docker, and the microservice architecture.
Let’s assume we’re building a payments platform. This platform will use three RESTful microservices: users-api, cards-api and transactions-api. The users-api allows our platform to register users. The cards-api assigns cards to those users. Lastly, the transactions-api handles the payments made when users swipe their cards.
But first, unit tests ☝️
To better understand Drone, let’s look at how you would run unit tests for the users-api microservice (the other services would run their unit tests similarly).
Let’s assume the users-api microservice is a Java app built using Gradle and uses a MySQL database managed by Flyway. This is what the Drone file (.drone.yml) would look like to run all unit tests.
# users-api/.drone.yml
kind: pipeline
name: users-api
steps:
- name: unit-tests
image: docker.mycompany.com/gradle-java:11
commands:
- gradle flywayMigrate # setup the database
- gradle test # run unit tests
services:
- name: users-database
image: mysql:5.7
Hopefully, this begins to show the power of Drone. In Jenkins, we would have to install Docker on our server and write Jenkinsfiles to setup pipelines that spin up containers — Drone does that automatically for us. All we provide are the Docker images.
Finally, integration tests 😎
When the unit tests pass, we know our service works as expected in isolation. But, what about its integration with the other two services of our platform? This is where integration testing comes in. Let’s say I have an integration test that creates a user, creates a card and simulates a transaction using that card. At this point, that test would fail because the users-api microservice can’t communicate to a cards-api or transactions-api microservice.
Drone allows us to add multiple items in the “services” section of the “.drone.yml” file.
# users-api/.drone.yml
kind: pipeline
name: users-api
steps:
- name: all-tests
image: docker.mycompany.com/gradle-java:11
commands:
- gradle flywayMigrate # setup the database
- gradle test # run unit tests
- gradle integrationTest # run integration tests
services:
- name: users-database
image: mysql:5.7
- name: cards-api
image: docker.mycompany.com/cards-api:latest
environment:
DB_HOST: cards-database
BRANCH: master
- name: cards-database
image: mysql:5.7
- name: transactions-api
image: docker.mycompany.com/transactions-api:latest
environment:
DB_HOST: transactions-database
BRANCH: master
- name: transactions-database
image: mysql:5.7
The result after we add the cards and transactions microservices to the “.drone.yml” file
Each microservice needs its own database, so those must be added as well. Now, if we had that integration test that creates a user, assigns a card to the user and simulates a transaction, that test would pass because all of the microservices can communicate with each other.
A word about Docker images ⚠️
The Docker images used for the containers that run a microservice (e.g. docker.mycompany.com/cards-api:latest) are essentially environments with a file system that contains the repo of the microservice and a script that spins up the microservice. The microservice can be spun up with a specific configuration that can be modified via environment variables(e.g. branch name, database host etc).
When the Docker container starts up, the initialization script runs and it spins up the application with the configuration provided. Creating and maintaining these Docker images is something that would have to be done for each microservice.
Join us! 😄
My journey at Marqeta so far has been incredible. I wouldn’t have had the opportunity to learn all of this (and much more) without my team’s support.
If you want to be part of this amazing company checkout our current openings!