By itself, the term “integration tests” is one of the most misleading: take a few programmers, ask what integration tests are, and everyone will tell you something different.
Examples of definitions:
- the test covers some kind of scenario (from http request to database requests), requests to the test database raised in testcontainers, external services are locked
- the test covers some scenarios (from http request to database requests), requests to the database and external services are blocked
- one service is being tested in a full-fledged test environment
- several services are being tested simultaneously in some kind of test environment
- one method is being tested that works with some kind of external system (DATABASE, external service), the external system is raised through testcontainers
I want to focus on the 1st-2nd option: this is the easiest way to raise the test coverage rate from zero percent to 60-70. But then each percentage will become more and more difficult.
Why is it simple? Each test passes a lot of code at once. Having covered the main positive and negative scenarios (usually there are few of them, otherwise they are not the main ones), we immediately see a good figure.
And what are the disadvantages?
- It starts up slowly. Still not a unit test.
- Combinatorial scenarios also appear easily: there are 3 choices on the first level, 3 more on the second, so you need to write 9 tests. Unlike 6, when tests are written in isolation.
- It is more difficult to cover non-core scenarios than the main ones, because nothing is ready for this yet: in order to avoid combinatorial effects, they switch from integration tests to unit, and nothing is ready there yet: neither the file itself has been created, nor the data.
What’s the way out? Test each public function of the class separately, locking all dependencies of the class:
- in this case, the combinatorial effect does not manifest itself, so it is quite obvious how to increase the coverage and it is linear in time
- technically, these are usually pure unit tests, which means they work fast
- when you need to test interaction with an external system, you can use testcontainers, but otherwise the same principle. In this case, the interaction has been tested, but there are a minimum number of such tests, which has a positive effect on the speed of the tests
- the approach does not impose restrictions on how to develop the system
Some people call it the London School of Unit testing.
As a result, the last definition for integration tests is closer to me.
At the same time, it is clear that the whole system as a whole also wants to be checked. To do this, it is better to use the so-called general performance tests (smoke tests). But we’ll talk about this in a separate article.