This is short tutoral about basic structure for unit tests with Entity Framework Core.
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, "David", "New York 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("David");
person.City.Should().Be("New York 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:
- XUnit – Test Framework
- FluentAssertions – Extension Methods for naturally assertions (like
.Should()
methods) - Moq – Best .NET mocking framework
- AutoFixture – injects values to test methods
- AutoFixture.AutoMoq – injects mocks to test methods
- AutoFixture.Xunit2 – Extensions for XUnit
- AutoFixture.SeedExtensions – great extensions for specific autofixture operations
Andriy Kravets is writer and experience .NET developer and like .NET for regular development. He likes to build cross-platform libraries/software with .NET.