George Kosmidis

Microsoft MVP | Speaks of Azure, AI & .NET | Founder of Munich .NET
Building tomorrow @
slalom
slalom

Integration Tests in ASP.NET Core API

by George Kosmidis / Published 4 years and 4 months ago, modified 4 years ago
Integration Tests in ASP.NET Core API

Automated testing is an important part of modern software production as it ensures higher quality and faster delivery, and on the same time it makes testing more affordable. Integration tests, sitting just between Unit Tests and E2E tests, improve test coverage and ensure proper communication between units. In this post, we will explore the possibilities of integration testing of ASP.NET Core by using xUnit to create a test for multiple endpoints.

What is Integration Testing

During the process of creating a smartphone, the display, the sensors, the lenses, the battery, all of the parts, are produced and tested separately (Unit Testing). When two or more units are ready, they are assembled and Integration Testing is performed. This process is similar to all engineering fields, including of course software engineering where integration tests ensure that an app’s components function correctly at a level that includes the app’s supporting infrastructure, such as the database, file system, and network.

ASP.NET Core supports integration tests using a unit test framework with a test web host and an in-memory test server, so some basic understanding of unit tests is assumed. If you are unfamiliar with test concepts and/or xUnit, check the Unit testing C# in .NET Core using dotnet test and xUnit.

What is xUnit

xUnit is the name of a collection of testing frameworks that became very popular among software developers. They derive their structure and functionality from Smalltalk’s SUnit, which was designed by Kent Beck in 1998. Since it was written in a highly structured object-oriented style, it was easy to be adapted to languages such as Java and C#.

You can read more on how to use xUnit in Microsoft Docs or in xUnit Documenation

Creating the test class

Version 2.1 of .NET Core introduced WebApplicationFactory<TEntryPoint> for bootstrapping an application in memory. This factory can be used to create a TestServer instance using the MVC application defined by TEntryPoint and one or more HttpClient instances used to send HttpRequestMessage to the TestServer. TEntryPoint is the entry point class of the System Under Test, usually the Startup class:

public class IntegrationParallelismTesting
{
    private readonly WebApplicationFactory<Startup> _factory;
    public IntegrationParallelismTesting(WebApplicationFactory<Startup> factory)
    {
        _factory = factory;
    }
}

If the SUT’s environment isn’t set, the environment defaults to Development.

Since sometimes –like with the WebApplicationFactory<TEntryPoint>– test context creation and cleanup can be very expensive, the context should be shared across all tests in a class. To achieve this, the test class must implement a class fixture interface (IClassFixture):

public class IntegrationParallelismTesting
        : IClassFixture<WebApplicationFactory<Startup>>
{
    private readonly WebApplicationFactory<Startup> _factory;
    public IntegrationParallelismTesting(WebApplicationFactory<Startup> factory)
    {
        _factory = factory;
    }
}

Creating a test

This is a trivial task on each own, since Theory and InlineData can help create one test for multiple endpoints. The example below uses _factory.CreateClient(); to create an instance of HttpClient, makes a call to the url passed as argument, and finally evaluates the results:

[Theory]
[InlineData("/")]
[InlineData("/Index")]
[InlineData("/Contact")]
public async Task Get_EndpointsReturnSuccessAndCorrectContentType(string url)
{
    // Arrange
    var client = _factory.CreateClient();

    // Act
    var response = await client.GetAsync(url);

    // Assert
    response.EnsureSuccessStatusCode(); // Status Code 200-299
    Assert.Equal("text/html; charset=utf-8", 
        response.Content.Headers.ContentType.ToString());
}

Using HttpClient with the using statement can cause a serious issue named ‘sockets exhaustion’. Read more about how to use HttpClientFactory instead!

Conclusion

Unit Tests vs Integration Tests, the battle continuous as it seems the line is not always so clear: Just replace a mock with the real object and you converted a Unit Test to an Integration Test – not always true…

There is a very nice detailed article by Stevens Anderson called Selective Unit Testing – Costs and Benefits that can help clear things out, but I personally always favor Integration Tests, and write Unit Tests in these cases only:

  • Algorithmic logic, e.g. calculations, business rules etc
  • Complex code, e.g. code with many dependenciess
  • Runtime error prone code, e.g. when using reflection
This page is open source. Noticed a typo? Or something unclear?
Edit Page Create Issue Discuss
Microsoft MVP - George Kosmidis
Azure Architecture Icons - SVGs, PNGs and draw.io libraries