WARNING — Unit Testing is not enough for your microservices
Many projects are adopting unit testing more frequently and this is awesome, but is it enough for a microservice architecture?
Let’s see why unit tests are not enough and what we can do to solve this problem.
Unit Test
Before talking about the problem the unit test does not solve, let’s define what is a unit test in this context.
The unit test can be considered the test for the smallest piece of code isolated.
This means that we must use a mock or stub to isolate the part that we would prefer not to test, and with it, we can test just our small unit.
Who has never seen this picture before, in this picture it seems more reasonable to create more unit testing, as it is easier to run and also cheap to create. However, for a microservice architecture, testing only the small pieces separated usually is not enough, we must guarantee the integration between the parts.
Let’s see one example:
Let’s imagine we have two microservices in our Pokémon application: a Trainer microservice responsible for managing trainers’ data and a Pokémon microservice responsible for managing Pokémon data. The Trainer microservice needs to retrieve a list of Pokémon owned by a trainer from the Pokémon microservice.
Now, when testing the Trainer microservice with unit tests, we might mock the Pokémon microservice’s API to return some sample Pokémon data. However, this doesn’t fully test how the Trainer microservice interacts with the Pokémon microservice over the network.
For example, our unit tests might not catch issues like slow network responses from the Pokémon microservice or errors in data transmission. These issues could lead to bugs or unexpected behaviour in the Trainer microservice, but our unit tests wouldn’t detect them because we’re only pretending to interact with the Pokémon microservice.
In this case, it’s essential to complement our unit tests with integration tests.
Integration Test
Before talking about the Integration Test, let’s define it.
The Integration Test aims to verify the interaction between different components, modules, or systems to ensure they function correctly together as a unified whole.
In our previous example, the Pokémon application, using Integration tests would involve setting up both microservices in a test environment and checking that they communicate correctly. This way, we can ensure that our microservices work together as expected in a real-world scenario, just like trainers and Pokémon do in the Pokémon universe!
This idea of using integration testing for these distributed applications is growing, and some big companies like Spotify redefined the pyramid, putting integration tests as more important in a microservice architecture, as we can see in the image below.
In the case of Spotify, they prefer to focus on the integration test instead of integrated testing, they define integrated testing as a test that “will pass or fail based on the correctness of another system.”
The integrated test is a more fragile way of testing, as changes to one system break the test from other systems.
Considering that, the integration testing will test the communication and the behaviours between two different services, but using fake services to provide similar behaviour. This is possible using tools like a mock server application, Testcontainers, and others.
Conclusion
Testing is a crucial activity to guarantee the software keeps working after fixing a bug or adding a new feature. The tests provide more confidence in the system by ensuring that behaviours work as expected.
Integration testing is essential for a microservices architecture where communication with multiple systems is common, and testing this communication is crucial to having confidence in the solution.
What do you think about it? Do you create integration testing for your microservices?