Integration testing with .NET Core

How backend can make QA’s life easier

Дмитрий Литичевский
Дмитрий Литичевский · 28 мая 2017
Backend · Teamlead

This article is based on my personal experience in backend development for Web API sites. Now I want to share it with other people.

When I’m speaking about integrartion testing I mean testing all components of the single Web API application. It often happens during the regression testing of the product performed by QA.

Possible approaches

There are several different approaches which can be used to perform the testing.

Handmade implementation

The most obvious way of performing integration testing of Web API application is sending request to instance of the application via Postman or another application with similar abilities and comparing responses with expected results.

Handmade realisation via Postman

Unfortunately it isn’t the best solution because it has several obvious disadvantages. Firstly, it takes a lot of time that means very slow developing process. Secondly, it is unreliable because human can forget something, can miss something, etc. Thirdly, you have to store information about product expected behavior in some form. It can be very simple and understandable by everyone, but in this case it can’t be rapidly changed, or it can be stored in very compact format and understandable by only a few people that is the cause of high bus factor.

Automated implementation

Many of mentioned problems can be solved by using tests automation. Well-written tests make the testing process much more stable and its results more predictable. Moreother, well-written tests can dramatically increase understanding of the operating principles of the system and reduce bus factor. For implementation of this approach a client of the tested service and a set of tests written in any programming language are needed.

Automated implementation via service client

Of course this approach solves a lot of problems mentioned early, but still it has some disadvantages. Firstly, running all tests still requires hours or even days because network interactions are very slow. Secondly, the instance of the tested service should be properly hosted in some environment, it can be developer’s computer or special test environment so it will be quite difficult to debug it if something goes wrong. Thirdly, there are still some application artifacts which correctness can be checked only by QA (for example, written logs, sent emails, messages pushed to queue). In this way testing process can’t be fully automated, QA still have to check some behavior manually, and it takes time.

Extended automated implementation

In the matter of fact, almost all problems mentioned in previous section are caused by application hosting. In fact, the necessity of manually checking part of the program’s work results is caused by the lack of the ability to replace the standard dependencies of the hosted system (such as logger, email client or queue client) with testable mocks. And of course the problem of the slow network interactions will be able to solve easily if client interacts with application directly in memory. Fortunately, now there is the special TestServer class which can solve all of these problems.

Automated implementation with TestServer via service client

In this approach tested application is hosted via TestServer, special server for tests purposes which performs Api methods calls directly in memory (it is avaliable in NuGet package Microsoft.AspNetCore.TestHost) and shared between all tests. Furthermore it is easy to debug tested application and change some of its dependencies because now Web API is hosted inside testing application. This briefly formulated approach contains many unexplained technical details, which will be minutely discussed in subsequent sections.

Technical details of the last implementation

Some small but important implementation details weren’t mentioned in previous section, so now they’ll be explained.

Why xUnit?

Hosting Web API site even via TestServer is an expensive operation. Moreover, several migration processes runned simualteniosly can corrupt the application environment. So its strictly necessary to setup one instance of the application, share it between all tests and dispose when all tests will pass. xUnit allows to do this by using collection fixtures. To use this approach folowing steps should be taken:

Final organization of tests

Of course this is not exactly integration testing because implementations of some standart contracts were changed (contacts of Logger, EmailClient and QueueClient) but they are usually developed and tested by other developers so this can’t do any harm.

Connection between client and server

The simplest way of establishing connection between Web API client and TestServer is to create the instance of the clinet inside the constructor of the tests class and pass the instance of HttpMessageHandler to it. Instance of HttpMessageHandler can be provided by method CreateHandler of the TestServer.

Code example

Base fixture for Web API tests collection

Base class for tests

Tests collection definition class

Web API tests example

References

Tools:

  1. https://www.nuget.org/packages/Microsoft.AspNetCore.TestHost;
  2. https://www.nuget.org/packages/xunit.

Documentation:

  1. https://docs.microsoft.com/en-us/aspnet/core/testing/integration-testing;
  2. https://andrewlock.net/introduction-to-integration-testing-with-xunit-and-testserver-in-asp-net-core/;
  3. https://xunit.github.io/docs/shared-context.html#collection-fixture.

Examples:

  1. https://github.com/litichevskiydv/WebInfrastructure/tree/master/src/Infrastructure/Web.Testing;
  2. https://github.com/litichevskiydv/WebInfrastructure/tree/master/test/Web.Tests.