Unit Testing with Entity Framework Core 3

Unit Testing with Entity Framework Core 3

This post covers the basic structure for unit tests with Entity Framework Core.

Note: Unit tests are only there to test the respective logic. Unit tests do not replace integration tests, especially for database operations.

Install EFCore In-Memory Provider

EFCore comes with its own InMemory Provider, which is explicitly intended for testing and not for production: Microsoft.EntityFrameworkCore.InMemory

Conveniently, NuGet offers the possibility to simply copy the necessary package reference as an XML entry in order to paste it directly into the project file:

1<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="3.1.0" />

Setup Test Database Context

The goal is that each test has its own virtual, In-Memory database to enable real isolation in the tests.

I simply use a Guid, which acts as database name. This means that each test can run in parallel, as each test gets its own database and thus its isolated infrastructure.

1
2// Setup Options
3string dbName = Guid.NewGuid().ToString();
4DbContextOptions<MyDbContext> options = new DbContextOptionsBuilder<MyDbContext>()
5                .UseInMemoryDatabase(databaseName: dbName).Options;

The database options can now be used in a test to build the respective context.

1    using (MyDbContext dbContext = new MyDbContext(options))
2    {
3        // your database stuff
4    }

Sample Test

It is generally recommended that adding and querying values in tests be done in different contexts.

Here is a complete example of an EF Core Test

  • Connection establishment
  • Adding values
  • Querying values
 1[Fact]
 2public async Task User_Should_Be_Added()
 3{
 4    // Setup
 5    string dbName = Guid.NewGuid().ToString();
 6    DbContextOptions<MyDbContext> options = new DbContextOptionsBuilder<MyDbContext>()
 7                    .UseInMemoryDatabase(databaseName: dbName).Options;
 8
 9    // Seed
10    using (MyDbContext dbContext = new MyDbContext(options))
11    {
12        PersonEntity person = PersonEntity.Create(1, "Batman", "Gotham City");
13
14        await dbContext.Persons.AddAsync(person);
15        await dbContext.SaveChangesAsync();
16    }
17
18    // Verify if insert works
19    using (MyDbContext dbContext = new MyDbContext(options))
20    {
21        PersonEntity person = await dbContext.Persons.SingleAsync(x => x.Id == 1);
22        
23        // verify reference
24        person.Should().NotBe(null);
25
26        // verify properties
27        person.Id.Should().Be(1);
28        person.Name.Should().Be("Batman");
29        person.City.Should().Be("Gotham City");
30    }
31}

In the case of a repository test, this code could be used almost 1:1.

Better Testing

For easier, better tests I recommend using the following libraries:


Comments

Twitter Facebook LinkedIn WhatsApp