Why should I use TestContainers approach within our integration testing inside our microservice/API?
By: Guilherme Rogatto
Tags: Testing, Technology, Test Automation, Microservices, Quality Assurance

Before we start let’s check the first pain point that when we create an automation project to assure our integration testing (microservice/API) with an environment dependency (like using development, staging or something like that). We’ll have a lot of problems and I can give some examples about that, let’s see:

A colleague changes something, pushes that code into that environment and then break our tests. One dependency isn’t working correctly (API gateway, database, queue stuff etc). We won’t believe anymore in our tests because we have a lot of flaky tests (they sometimes pass and sometimes fail). We need a lot of uptime in our environments (if we have to run at 3 a.m. that automation, will the environment dev and staging be up for that or do I have to be up for that environment manually this time?). We have a lot of problems into the pipeline (CI/CD) because of all the points that I’ve mentioned above.

So, my colleague, our problems have finished. We can build and simulate a whole environment that our application needs with TestContainers, some like databases, queues tools, keycloack or any other authentication tool and an infinity of containers that we can use to make all tests to assure our endpoints/resources/behavior/responses from our microservices. And when that has finished we can stop all containers at the end of the execution. The main point here is that we can use this concept with any programming language (Java, NodeJS, Python, C#/dotnet and others). Let’s see a code example structure:

Code example using Java with Spring Boot Application:

Starting dynamo database with TestContainer using the image amazon/dynamodb-local:latest:

2. Starting the Microservice (Spring Boot Application) with the database that we started using TestContainers and with Sprint Boot Test Property passing the dynamos’s endpoint:

3. Before starting the test we must setup the dynamoDB with some index or elements (in this case we used a NoSQL Database). There is a class that I use AWS library to put the necessary structure DynamoDataBase.createDynamoDataBase:

4. So, now in the test method using JUnit or other unit testing tools we can make HTTP request using any library (RestAssured, RestTemplate) that the programming language provide us and test our microservice/API.

Finally, we can easily concern some good points using these TestContainers approaches:

We won’t spend money on maintenance of any environment uptime 24 hours per day. We have our own environment all time that we want. The developer will help us more frequently to assure that the tests will pass because whole test structure will be in the same repository. We can setup a mock to external integrations in an easily way. (using WireMock for example). We will trust much more in our tests into the pipeline or in any other execution.

Some points that we really need to pay attention to using pipelines with this TestContainers approach:

The agent pipeline will start all containers locally that we need for our tests, but imagine a lot of projects starting and finishing this kind of thing. So we can sometimes have a problem trying to start a container with the same port or the agent pipeline doesn’t have a lot of memory to parallel jobs, it can be a problem.

Link to TestContainer: https://www.testcontainers.org/