Do you know when unit tests start to be integration tests? I bet you will be surprised!

This catchy title came from my interviewer experience. One of the questions I ask during the interviews is as follows: “In what cases do we say about the unit test that it is already an integration test”. Most candidates can easily name one case, but there are few more. A good source of knowledge in this case is the book Unit Testing Principles, Practices, and Patterns in which we can find 2 more cases. Are you curious? I hope so!

Communication between modules

Let’s start with the most obvious case. When our unit test verifies communication between two independent modules or communication with external systems such as queues, databases or a mail server, then we are dealing with an integration test. An example of such a test:

[Theory]
[InlineData(EXPECTED_REPORT_FILE)]
public void Should_Generate_PredictionReport_For_User(string expectedReportFile)
{
	var userManagementModule = new UserManagementModule();
	var reportingModule = new ReportingModule();
	var processingModule = new ProcessingModule();

	userManagementModule.Initialize();
	reportingModule.Initialize();
	processingModule.Initialize();

	SetUp(userManagementModule, reportingModule, processingModule);

	var user = userManagementModule.GetUser(MOCK_ADMIN_ID);
	var prediction = processingModule.GeneratePrediction(user, MOCK_START_DATE, MOCK_END_DATE);
	var report = reportingModule.GenerateReport(MOCK_REPORT_TYPE, prediction, user);

	var filePath = Path.Combine(MOCK_REPORT_FILE_PATH, report.FileName);
	using var fileStream = File.Create(filePath);
	report.Stream.Seek(0, SeekOrigin.Begin);
	report.Stream.CopyTo(fileStream);

	var actualFileHash = HashHelper.GetFileHash(filePath);
	var expectedFileHash = HashHelper.GetFileHash(expectedReportFile);

	Assert.Equal(expectedFileHash, actualFileHash);
}

The test uses 3 different modules to generate a report and compare it with the reference one. A real database is used to retrieve user data, and a tool for generating pdf files is used to generate report. For such reason, this test significantly exceeds what we understand as unit tests, and additionally we have here communication between different modules.

Slow tests

This case is due to how we use unit tests. Their task is to give us quick feedback on whether a given unit works as intended or not. We should run them often, even several times within an hour. On this assumption, slow tests would be a big problem. What exactly a slow test means can be understood in different ways, some will say that waiting 30 seconds for a test suite to complete is too much, while others may say that this limit may be shifted to as much as 1-2 minutes. Personally, I think that when one test takes more than 1 second, it’s a good candidate to become an integration test. Example of test execution times:

There is one test that takes 2.3 seconds to complete, which is a good candidate to be moved to the integration test suite.

Tests that tend to be performed for a longer period of time usually cover the generation of reports, charts or other graphical files and logics in which it’s required to wait a certain time. In that case, even if the test doesn’t go beyond a single unit, we would prefer to run such tests separately from our unit tests due to the time they take.

Tests without isolation

Another assumption about unit testing is that we can execute them in any order and the result should always be the same. However, sometimes there is a need to perform tests in a specific order, e.g. in one test we check the mechanism of adding data, and in another – displaying data with filtering. When writing such tests from the very beginning, you should think that the order in which the tests are run will strongly affect the results, but in the case of integration testing is not that dangerous. In the end, there is a good chance that we have prepared a suitable test suite in which we have determined correct order. An example of such tests:

The tests check methods from the UserService. The service has methods to create, retrieve, filter and delete users. These tests are not isolated, so the Should_Create_User test will leave the data of the created user and the result of the Get_All_Users test will depend on whether the Should_Create_User test has already been run or not. The Should_Remove_User test can also affect results.

These are the 3 cases which suggest that the written tests are integration tests, not the unit tests. Usually, when we deal with integration tests, we see them as the first case, i.e. testing integration between two modules, while the other two cases are not so obvious and probably require more time to discover them. If you have in your unit tests some tests that suit better as integration tests, I think it would be a good idea to move them to the integration test suite and run them through CI when you want to validate your pull request instead of running it during development.

I hope you found this post interesting! I also invite you to visit other posts, especially ones from the unit tests code smells series, where I describe frequent code smells that occurs when writing unit tests. If you find this post valuable, please share it with others so that it can reach more people.

1 thought on “Do you know when unit tests start to be integration tests? I bet you will be surprised!”

  1. Pingback: dotnetomaniak.pl

Leave a Comment

Share via
Copy link
Powered by Social Snap