/asp-net-core-integration-testing-demo

A repo with a demo of how to do repetitive ASP.NET Core integration testing including a database using transations

Primary LanguageC#

Example of Integration Testing with ASP.NET Core including a database

This repo contains code that demonstrates how you can do integration testing with ASP.NET Core applications that include authentication and databases.

The code is explained in my blog post Integration Testing with ASP.NET Core, so if you are curious, I suggest having a look at that!

Background

Having a database often introduces some complexity when it comes to running integration tests, as the data in the database needs to be consistent. This is obviously something that might become a bit complicated when you have tests that add or delete data. Not to mention, that the data needs to contain all possible permutations needed to perform the required tests. Something that often leads to complications when new areas are tested, which requires changes to old tests, due to the data being updated to include the data required for the new tests.

In this sample, the code uses a transaction around each test, allowing the database to be populated with required data before each test, and then rolled back to its original "empty" state when completed. This also allows you to validate the contents in the database as part of the test if needed. Without seeing data from other tests that are being run.

Caveat: There might be edge cases where the transactions cause deadlocks. However, so far, this has not been observed while using this way of working.

The sample also includes some code to handle authentication when doing ASP.NET Core integration. In this particular example, the solution adds a separate Basic Auth scheme, and updates the default authorization policy to check the new scheme as well. This should work in a lot of cases. However, in some cases, a more complex solution might need to be implemented.

Source code

The source code consists of 2 applications, a Web API that is to be tested, and a test project using xUnit to run the tests.

The test project contains 3 different versions of tests. The first and most simple, is the UsersControllerTests class. This class does all set up inside a helper method in the test class.

However, as this becomes tedious and annoying if there are many test classes, there is a cleaner version called UsersControllerTestsWithTestBase, which uses 2 base classes to perform the set-up. This version also uses a more functional syntax, allowing each test method to be a bit cleaner, while still able to perform everything it needs.

Finally, there one version called UsersControllerTestsWithTestHelper that uses a helper class to do the set-up. The helper class is using a fluid syntax that gives the test methods an easy way to set up and run the tests as needed. This implementation also enables the use of extension methods to add custom functionality.

Note: The TestHelper is a quick simple implementation that probably needs a bit more work. But it is there for you to see.

Running the tests

The tests are dependent on a database (obviously). The easiest way to set this up is to run the following command, and start a Docker container

> docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=P@ssword123" \
   -p 14331:1433 --name sql_test --hostname sql_test \
   -d mcr.microsoft.com/mssql/server:2019-latest

This creates a new SQL Server Docker container that exposes the server on port 14331 instead of the default 1433. This way, it won't interfere with any existing SQL Server on your machine.

Before each test run, EF Core is used to ensure the database is created, and then the applications migrations are applied to the database. This is done in the TestRunStart class, which uses the Xunit.TestFramework attribute to get it to run during each test run.

Feedback

I am always interested in feedback, so if you have any, feel free to reach out on Twitter. I'm available at @ZeroKoll.

When it comes to this code, I'm curious about your preference when it comes to using a base class or a helper. I'm torn myself, so feedback would be very much appreciated!