Test as you run (Part 5) – Building and Testing Systems

In the previous part of our series about test-driven development of microservices we created some debug configurations and integration tests with all dependencies in place. This is the fifth part.

The first can be found here.

Let’s have a look on how to test a whole system.

Please checkout step 6:

git checkout step6

A new Service was added to the project called the BFFService that is basically facade or simplistic BFF for the other two services. It uses the UserService to store users and the CapitalizeService to capitalize the users first- and lastname.

Unit Tests first

The project has two testing sourcesets: test and integrationTest.
The testing sourceset contains small Unit-tests for the BFFService logic to check the interaction with the UserService and the CapitalizeService.

Let’s have a look into (a snippet) of this test:

The RestClients used to communicate with the UserService and CapitalizeService are mocked in lines 6 and 7 and interactions are verified based on these mocks.

Check the System

Okay, so we have created a service that interacts with other services and we created some unit tests for these interactions. That’s nice and it helps us to find problems early, but it’s not enough for testing the the system created out of the three services. So let’s have a look at the integration test:

The test looks a lot as we expected; it’s a server-stack test checking the service with real requests. For this tests all dependencies of the BFFService have to be in place so let’s have a look into (a snippet of) the BFFService build.gradle:

The integrationTest task depends on docker-compose and uses the following compose-file:

version: '3'
      image: usercassandra:latest
          - 9042
      image: testasyourun/userservice:integrationtest
          - 4583
              - cassandra
        - cassandra.host=cassandra
        - cassandra.keyspace=test
        - cassandra.port=9042
           image: testasyourun/capitalizeservice:integrationtest
            - 3491

By orchestrating the docker containers shown above the CapitalizeService, the UserService, and the testdata Cassandra are started and the UserService get’s automatically connected to the Cassandra. By having a look at the setDependenciesEnv method in the build.gradle we see that all necessary information of the two services directly used by the BFFService are passed to it throw the environment as we’ve seen before. That’s pretty straight forward.

Where do all these images come from

We want to have docker images of the UserService and the CapitalizeService to use them for the integrationtests of the BFFService. To build these docker-images we use the gradle docker plugin as you can see in the root build.gradle:

classpath 'se.transmode.gradle:gradle-docker:1.2'

Futhermore the service.gradle file was extended:

A task was added to every service using the docker plugin to create a docker image. Executing this task will give us the images defined in the above docker-compose file used by the integrationtests of the BFFService.

To ensure that the images of the UserService and the CapitalizeService are build before they are used by the BFFServices integration test the tasks are defined as dependencies of the integration-test-task (see the lines 30–33 in the BFFServive build.gradle). If we want to test and build our complete system now, all we have to do is to type:

./gradlew build

into our console :-)

Let’s sum it up

Testing Microservices in every reasonable scope is quite easy if we follow some best practices:

  • separate your business logic from the framework specific logic and test it with unit tests.
  • Test the small scopes first with some advanced mock frameworks well fitting to your technology stack
  • create fully automated integration tests against your complete setup

I hope you enjoyed the article and I would be glad to get some feedback.

I’ve you miss some explanation please have a look into the git-repository since it contains some more configurations and tests I haven’t explained here in detail.

About Olaf Gunkel

Olaf is a fullstack software craftsman and the head of the web-engineering department. He deeply cares about software craftsmanship, code quality and agility. He publishes articles and gives talks about softwarecraftsmanship, microservices, test driven development and parallelism on a regular basis.