SHARE:

ASP.NET Core 5 & EntityFramework Core: Clean, clear and fluent integration tests with Calzolari.TestServer.EntityFramework, FluentAssertion.Web and xUnit

Introduction

Not long ago I was asked to do integration tests with ASP.NET Core and Entity Framework Core and I was confronted with some difficulties, in particular the fact of testing Entity Framework Core in integration? I have not found a simple tutorial to create clear and simple tests and especially how to get around the limitations of Sql in memory when using EntityFramework Core. So I had to find work arounds with SqlLite to do my tests while making sure that there are no data collisions in each test. In addition, I wanted to make sure that I was using AAA (Arrange, Act, Assert) in such a way as to maintain optimal ideal readability. So I had the idea to create the Calzolari.TestServer.EntityFramework library which encapsulates all the management of EntityFramework Core in integration, this library also uses Flurl for writing Http clients in a fluid way and FakeBearerToken allowing to pass tokens in the simplest way in endpoints protected by JWTs. In this article I will also show how to use Calzolari.TestServer.EntityFramework in conjunction with FluentAssertions.Web which is an assertion library for the web and xUnit in order to perform really clean integration tests! I used as well AutoFixture to facilitate somme Arrange.

Note that Parallelism MUST BE deactivated while testing, the database is created and removed after each test which avoids any data collision, the DbContext is reinstancianted between each test. It’s not recommended for large databases.

Test scenario

We’ll consider the following scenario, an endpoint protected by a JWT which gets a country by its Id, and the second one protected with a JWT that must contains the Admin role which creates a country:

Setup your integration test project

Install first the following packages, for example in command line in the Package Manager console:

Install-Package Calzolari.TestServer.EntityFramework
Install-Package AutoFixture
Install-Package FluentAssertions.Web
Install-Package xunit
Install-Package xunit.runner.visualstudio
Install-Package Microsoft.AspNetCore.Mvc.Testing
Install-Package Microsoft.NET.Test.Sdk

Don’t forget to import as reference the ASP.NET Core project you want to test.

Create a dedicated Startup.cs file for your integration tests

I strongly suggest to create specific Startup.cs file (name it TestStartup.cs for example) for better clarity, don’t use the Startup.cs file from the web project you want to test.

Then :

  • Add the method AddApplicationPart which takes in parameter any class of your webapplication (for example the Startup class), this is needed to retrieve controllers and register them in the integration test project
  • Register the DbContext with the following method: AddIntegrationTestDbContext, this is needed for integration test to be working with EF Core, behind the scene it register your DbContext with SQLite Database
  • Register the fake bearer token authentication with the AddFakeBearerToken method, this is needed to create an identity with a fake token while performing integration tests

It should look like this:

Create a Web factory to create the test server

Inherit from FlurlWebFactory, generic T can be any class of your webapplication (for example the Startup class, the reason why is explanied in a previous post here: TestServer & ASP.NET Core 5: Fix “System.InvalidOperationException : Solution root could not be located using application root” with a custom Startup file – Anthony Giretti’s .NET blog), then override CreateHostBuilder and use your TestStartup.cs file like this:

Create a xUnit collection definition

A collection definition allows injection dependency (Singleton lifetime) within your test classes. The following example shows how to register a collection named “AssemblyFixture” that allows to pass into your test classes the TestFactory we defined just before:

Create a base test class

Inherit from IntegrationTestBase<TDbContext, T>, TDbContext is your DbContext and T is the same class that you defined as generic parameter on FlurlWebFactory when you wrote your web factory, then, add [Collection(“AssemblyFixture”)] attribute on your base test class. This is here where you can add AutoFixture or anything else that you need for your all your tests. Your base test class should look like this:

Write your tests

Create a test class and inherit from the base test class. Pass the web factory by injection dependency and write your tests. I suggest to use FluentAssertions for your assertions

To feed the database use the method Arrange like this

var countries = Fixture.Build<Country>()
                       .CreateMany(3);
  
Arrange(dbContext =>
{
    dbContext.Countries.AddRange(countries);
});

If you expect an auto incremented Id, it’s filled automatically like this:

var country = Fixture.Create<Country>();

Arrange(dbContext => { dbContext.Countries.Add(country); });

// country.CountryId CountryId is filled

Create a fake token to be authenticated while calling the endpoint to test, sub is the claim that represents the identity within a JWT:

var token = new
{
    sub = "Anthony Giretti"
};

Then call your System Under Test (SUT):

var response = await BASE_REQUEST.Route(BaseRoute).FakeToken(token).GetAsync();

And assert the result by using FluentAssertions.Web:

response.ResponseMessage
	.Should()
	.Be200Ok()
	.And
	.BeAs(countries);

The response is typed as IFlurlResponseMessage, whichs exposes the native HTTP ResponseMessage , that’s why I had to write response.ResponseMessage to get the HTTP response.

The test class should look like this:

You can find more example here, especially with Authorization based on Roles on a POST endpoint:

Conclusion

This post aimed to show how you could test your ASP.NET Core application which uses EntityFramework Core, as you can see, all the complexity brought by EntityFramework Core in integration testing has been encapsulated. Don’t forget it’s not recommanded for larage database, on my end, I’m testing my microservices like this.

I hope you liked this post, and if you have any suggestion, for example, adding new features in Calzolari.TestServer.EntityFramework, feel free to send me an email :).

Written by

anthonygiretti

Anthony is a specialist in Web technologies (14 years of experience), in particular Microsoft .NET and learns the Cloud Azure platform. He has received twice the Microsoft MVP award and he is also certified Microsoft MCSD and Azure Fundamentals.