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#.
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
TEntryPoint is the entry point class of the System Under Test, usually the
If the SUT’s environment isn’t set, the environment defaults to
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):
Creating a test
This is a trivial task on each own, since
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:
usingstatement can cause a serious issue named ‘sockets exhaustion’. Read more about how to use
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