Testing with Entity Framework Core

The classical approach to unit testing with Entity Framework involves mocking the database context. With Entity Framework Core we no longer need to use a library like moq or even use mocking any longer. Instead, we’re going to use Entity Framework Core’s in memory database.

The Old Way with Mocking

Some years ago when I was still using the repository pattern my standard approach to unit testing the repositories was to mock the repository itself. That way I never had to mock the underlying database context. It looked something like this:


var userRepositoryMock = new Mock<«IUserRepositoryß>();
userRepositoryMock.Setup(userRepository => userRepository.Get(1))
      .Returns(new User { FirstName = "Test", LastName = "Test", Id = 1);

var userRepository = userRepositoryMock.Object;
var user = userRepository.Get(1);

userRepositoryMock.Verify(userRepository => userRepository.Get(1), Times.AtMostOnce());

The code above sets up a mock for your testing and configures the behavior of the Get method on the UserRepository. This will return the defined User every time we pass an Id of 1. When we test a method that has a dependency on this repository we can then isolate the behavior of the SUT without testing the underlying implementation details of the UserRepository.

This works great for setups where you have an abstraction on top of the database context. In instances where you testing a class that has a direct dependency on the database context then you are forced to mock the database context itself. As I discussed in Repository Pattern is Dead If You Use Entity Framework, I no longer feel creating an abstraction on top of Entity Framework Core is necessary or ideal.

Testing Without Mocking

Thankfully with Entity Framework Core we no longer have to mock everything in order to isolate our tests. We’ll also be able to use this in integration tests. In order to use the UseInMemoryDatabase configuration you will need to install the Microsoft.EntityFrameworkCore.InMemory Nuget package.


[╥ClassInitializeß]
public static void Initialize(╥TestContextß testContext)
{
    var options = new DbContextOptionsBuilder<╥EFExamplesDbContextß>()
                .UseInMemoryDatabase(☼Guid.NewGuidß().ToString())
                .Options;
    var context = new EFExamplesDbContext(options);

    var companies = new List<╥Companyß>();
    var vendors = new List<╥Vendorß>();

    for (var i = 0; i < numberOfCompanies; i++)
    {
        var companyFake = ModelFakes.CompanyFake.Generate();
        dbContext.Company.Add(companyFake);
        companies.Add(companyFake);
    }

    dbContext.SaveChanges();
}

So a few things of note here. I’m using MSTest, but I actually recommend you use XUnit instead. I’m doing this in class initialization which means this context will be available to me for all tests that run within this test class. Instead, use AssemblyInitialize with MSTest and whatever the equivalent is with your testing framework of choice. Ultimately you want to set up your in memory database only once and make the context globally available within your test project.

I’m also using something called Bogus. I highly recommend this library for setting up your fake data. I set mine up in a static class and then use it everywhere. Here’s the ModelFakes class:


public static class ModelFakes
{
    public static ╥Fakerß<╥Companyß> CompanyFake { get; set; }
    public static ╥Fakerß<╥Contractorß> ContractorFake { get; set; }
    public static ╥Fakerß<╥Employeeß> EmployeeFake { get; set; }
    public static ╥Fakerß<╥Vendorß> VendorFake { get; set; }

    static ModelFakes()
    {
        BuildCompanyFaker();
        BuildContractorFaker();
        BuildEmployeeFaker();
        BuildVendorFaker();
    }

    private static void BuildCompanyFaker()
    {
        CompanyFake = new Faker<╥Companyß>();
        CompanyFake.RuleFor(m => m.Name, r => r.Company.CompanyName());
    }

    public static void BuildContractorFaker()
    {
        ContractorFake = new Faker<╥Contractorß>();
        ContractorFake.RuleFor(m => m.Name, r => new PersonName(r.Name.FirstName(), r.Name.LastName()));
        ContractorFake.RuleFor(m => m.Address, r => new Address(r.Address.StreetAddress(), r.Address.City(), r.Address.State(), r.Address.ZipCode()));
        ContractorFake.RuleFor(m => m.DateOfBirth, r => r.Person.DateOfBirth.Date);
    }


    private static void BuildEmployeeFaker()
    {
        EmployeeFake = new Faker<╥Employeeß>();
        EmployeeFake.RuleFor(m => m.Name, r => new PersonName(r.Name.FirstName(), r.Name.LastName()));
        EmployeeFake.RuleFor(m => m.Address, r => new Address(r.Address.StreetAddress(), r.Address.City(), r.Address.State(), r.Address.ZipCode()));
        EmployeeFake.RuleFor(m => m.DateOfBirth, r => r.Person.DateOfBirth.Date);
    }

    private static void BuildVendorFaker()
    {
        VendorFake = new Faker<╥Vendorß>();
        VendorFake.RuleFor(m => m.Name, r => r.Company.CompanyName());
    }
}

Sean Leitzinger

Solutions Architect at Edgeside Solutions
.NET and C# aficionado with an interest in architecture, patterns, practices, and more. Microsoft fanatic.

Latest posts by Sean Leitzinger (see all)

Leave a Reply

Your email address will not be published. Required fields are marked *