Integration tests

Testing is a staple of software engineering that has many benefits. There are two that are particularly important.

The most obvious benefit is that it allows you to verify the correctness of code, particularly in a team environment where you are often making changes to unfamiliar code. A test should clearly and explicitly validate the relevant business logic.

The second benefit is their ability to speed up the development process by moving the feedback loop earlier through local validation of changes. The feedback loop though, can only be useful if the local tests are as close to the production environment as possible. Unit tests that mock out every dependency are useless and provide no feedback.

Mocking

From experience, mocking is used extensively in C# projects. However, while mocking is simple, it removes vital context. The issue lies in the fact that, by relying heavily on mocking, you're essentially removing the real implementation that's supposed to do the heavy lifting during runtime. There are scenarios like I/O, or crossing the domain boundary where mocks should still be used but they should not be the default choice in a test.

If you imagine a test that uses a database. Instead of mocking out the communication with the database, a docker container could be used to spin up a local instance. By interacting with the container the tests can cover the connection managment, serialisation/deserialisation and data correctness of any queries.

Integration tests

There is a statement in the ASP.NET Core documentation for Integration tests:

In contrast to unit tests, integration tests:
  • Use the actual components that the app uses in production.
  • Require more code and data processing.
  • Take longer to run.

Therefore, limit the use of integration tests to the most important infrastructure scenarios. If a behavior can be tested using either a unit test or an integration test, choose the unit test.

Integration tests are the antitode to mocking, they make network calls, talk to databases and "use the actual components that the app uses in production", all of which can be overlooked when mocking out dependencies.

By making a network call to the service under test we can test behaviour as a consumer of the API, as it would behave in production.

The Testing Pyramid is outdated, integration tests can be run much quicker and while they are still more computationally expensive than unit tests the gap is closing. As Integration tests can be run locally the feedback loop is virtually the same as unit tests, there is very little difference from a time perspective and given the relative increase in coverage from integration tests it is difficult to justify writing a unit test in place of an integration test.