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()); } }
Noel
July 16, 2020Hi Sean,
Thanks for the article.
FYI, The link at ‘Repository Pattern is Dead If You Use Entity Framework’ points to the wpadmin section and not to the article itself.
Regards
Noel
Sean Leitzinger
July 16, 2020Hi Noel. Thanks for pointing that out. Fixed it.