Picture: Microsoft

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:

<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.


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

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

    using (MyDbContext dbContext = new MyDbContext(options))
    {
        // your database stuff
    }

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
[Fact]
public async Task User_Should_Be_Added()
{
    // Setup
    string dbName = Guid.NewGuid().ToString();
    DbContextOptions<MyDbContext> options = new DbContextOptionsBuilder<MyDbContext>()
                    .UseInMemoryDatabase(databaseName: dbName).Options;

    // Seed
    using (MyDbContext dbContext = new MyDbContext(options))
    {
        PersonEntity person = PersonEntity.Create(1, "Batman", "Gotham City");

        await dbContext.Persons.AddAsync(person);
        await dbContext.SaveChangesAsync();
    }

    // Verify if insert works
    using (MyDbContext dbContext = new MyDbContext(options))
    {
        PersonEntity person = await dbContext.Persons.SingleAsync(x => x.Id == 1);
        
        // verify reference
        person.Should().NotBe(null);

        // verify properties
        person.Id.Should().Be(1);
        person.Name.Should().Be("Batman");
        person.City.Should().Be("Gotham City");
    }
}

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: