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.