While unit-testing EF Core applications, sometimes, we may want to mock EF Core DbContext. In this article, we’ll talk about a few ways of doing that.
Let’s get going.
When to Mock EF Core DbContext
There are different ways in which we can test an application that uses the EF Core database. The first and foremost option is to test it against an actual production database. This ensures that the application behaves as expected in production as well. However, it is not always feasible to test an application with an actual production database. This brings us to the next option – testing our application using some fake data.
If we are planning to use fake test data, then the best option is to implement a repository pattern and mock the data access layer. By implementing this, we can have a clear separation between the data access layer and the business logic of our application. Apart from that, we can easily test our application as well using this pattern.
However, if implementing a repository pattern is not viable for an application, then we can think of either using a fake EF Core provider like SQLite, In-Memory, etc., or mocking the DbContext
.
We are going to learn how to mock an EF Core DbContext
by using two popular libraries:
- Moq.EntityFrameworkCore
- MockQueryable
Without further ado, let’s start.
Preparing the Environment
First, let’s configure an EF Core application.
As explained in the linked article, let’s create an Employee
model for this example:
public class Employee { public int Id { get; set; } public string Name { get; set; } public string Email { get; set; } public string Phone { get; set; } }
After creating the context file and updating the database, let’s create a controller with two action methods:
public class EmployeesController : ControllerBase { private readonly EmployeeDBContext _context; public EmployeesController(EmployeeDBContext context) { _context = context; } // GET: api/Employees [HttpGet] public async Task<ActionResult<IEnumerable<Employee>>> GetEmployees() { return await _context.Employees.ToListAsync(); } // GET: api/Employees/5 [HttpGet("{id}")] public async Task<ActionResult<Employee>> GetEmployeeById(int id) { var employee = await _context.Employees.FindAsync(id); if (employee is null) { return NotFound(); } return employee; } }
Here we have the GetEmployees()
method that returns all employee records and the GetEmployeeById()
method that fetches the employee record with the specified Id
.
Feel free to skip creating a repository layer in this example as our focus in this article is on mocking EF Core DbContext
when implementing a repository pattern is not feasible.
How to Mock EF Core DbContext Using Moq.EntityFrameworkCore
Now, let’s take a look at how to mock EF Core DbContext
using the Moq.EntityFrameworkCore library. For that, let’s install the Moq.EntityFrameworkCore
NuGet package:
Install-Package Moq.EntityFrameworkCore
This library will help us to mock EF Core contexts and we can test methods that are using the DbContext
, DbSet
, etc.
We can reference the library by including the Moq.EntityFrameworkCore
namespace:
using Moq.EntityFrameworkCore;
Now, let’s write a test for the GetEmployees()
method:
[Fact] public async Task GetEmployees_WhenCalled_ReturnsEmployeeListAsync() { // Arrange var employeeContextMock = new Mock<EmployeeDBContext>(); employeeContextMock.Setup<DbSet<Employee>>(x => x.Employees) .ReturnsDbSet(TestDataHelper.GetFakeEmployeeList()); //Act EmployeesController employeesController = new(employeeContextMock.Object); var employees = (await employeesController.GetEmployees()).Value; //Assert Assert.NotNull(employees); Assert.Equal(2, employees.Count()); }
Here we mock the DbContext
and set up the Employee DbSet
to return a fake employee list:
private static List<Employee> GetFakeEmployeeList() { return new List<Employee>() { new Employee { Id = 1, Name = "John Doe", Email = "[email protected]", Phone = "123-456-7890" }, new Employee { Id = 2, Name = "Mark Luther", Email = "[email protected]", Phone = "123-456-7890" } }; }
Now, what about testing the GetEmployeeById()
method? This method uses the DbSet.FindAsync()
method for fetching an employee record and we need to mock that method. But Moq.EntityFrameworkCore
doesn’t provide any inbuilt way of mocking the FindAsync()
method. However, the good news is that we can still test this by mocking the DbContext
and setting up the FindAsync()
method:
[Fact] public async Task GetEmployeeById_WhenCalled_ReturnsEmployeeAsync() { // Arrange var employeeContextMock = new Mock<EmployeeDBContext>(); employeeContextMock.Setup(x => x.Employees.FindAsync(1).Result) .Returns(TestDataHelper.GetFakeEmployeeList().Find(e => e.Id == 1) ?? new Employee()); //Act EmployeesController employeesController = new(employeeContextMock.Object); var employee = (await employeesController.GetEmployeeById(1)).Value; //Assert Assert.NotNull(employee); Assert.Equal(1, employee.Id); }
Here we mock the DbContext
and set up the FindAsync()
method of the DbSet
to return a particular employee record when we pass a particular Id
. Using this, we can test the GetEmployeeById()
method.
How to Mock EF Core DbContext Using MockQueryable
The MockQueryable library provides extensions for mocking EF Core operations like ToListAsync()
, FindAsync()
, etc. It works pretty well with mocking libraries like Moq, NSubstitute, and FakeItEasy. Now let’s see how to mock EF Core DbContext
using the MockQueryable
library for the same example.
For using MockQueryable, first, we need to install the NuGet package corresponding to the mocking library that we use. Since we are using Moq, let’s install MockQueryable.Moq
package:
Install-Package MockQueryable.Moq
Similarly, if we are using NSubstitute, we can use the MockQueryable.NSubstitute
package and with FakeItEasy, we can use the MockQueryable.FakeItEasy
package.
After that, we can reference the library by including the specific namespace, in our case the MockQueryable.Moq
:
using MockQueryable.Moq;
Now, let’s write a test for the GetEmployees()
method:
[Fact] public async Task GetEmployees_WhenCalled_ReturnsEmployeeListAsync() { // Arrange var mock = TestDataHelper.GetFakeEmployeeList().BuildMock().BuildMockDbSet(); var employeeContextMock = new Mock<EmployeeDBContext>(); employeeContextMock.Setup(x => x.Employees).Returns(mock.Object); //Act EmployeesController employeesController = new(employeeContextMock.Object); var employees = (await employeesController.GetEmployees()).Value; //Assert Assert.NotNull(employees); Assert.Equal(2, employees.Count()); }
Here, first, we build a mock DbSet
using the fake employee list. After that, we set up the DbContext
to return this DbSet
. This way, we can test the GetEmployees()
method by using a mock DbSet
.
Next, we are going to test the GetEmployeeById()
method. As we mentioned earlier, this method uses the DbSet.FindAsync()
method for fetching a specific employee record. The good news here is that the MockQueryable library provides inbuilt support for mocking the FindAsync()
method of the mock DbSet
that we create. So let’s write a test by mocking the DbSet.FindAsync()
method:
[Fact] public async Task GetEmployeeById_WhenCalled_ReturnsEmployeeAsync() { // Arrange var mock = TestDataHelper.GetFakeEmployeeList().BuildMock().BuildMockDbSet(); mock.Setup(x => x.FindAsync(1)).ReturnsAsync( TestDataHelper.GetFakeEmployeeList().Find(e => e.Id == 1)); var employeeContextMock = new Mock<EmployeeDBContext>(); employeeContextMock.Setup(x => x.Employees) .Returns(mock.Object); //Act EmployeesController employeesController = new(employeeContextMock.Object); var employee = (await employeesController.GetEmployeeById(1)).Value; //Assert Assert.NotNull(employee); Assert.Equal(1, employee.Id); }
Here, we create a mock DbSet
and set up the FindAsync()
method. Next, we set up a mock DbContext
using this DbSet
. Now since we have mocked the DbSet.FindAsync()
method, we can easily test the GetEmployeeById()
method.
Moq.EntityFrameworkCore vs MockQueryable
We use the Moq.EntityFrameworkCore for mocking EF Core contexts. By using it, we can easily test methods that are using the DbSet
or DbQuery
from the DbContext
. However, this library supports only the Moq library for mocking. So if we are using Moq and we just want to mock a DbSet
or DbQuery
, then it makes perfect sense to use this library. On the other hand, if we want to use other mocking libraries like NSubstitute or FakeItEasy or if we want to mock EF Core query operations, then this library will not work.
MockQueryable provides extensions for mocking several EF Core operations such as FindAsync()
, ToListAsync()
, FirstOrDefaultAsync()
, etc. It helps us in building a mock DbSet
and then set up these EF Core methods inside it. This is very helpful in testing methods that implement EF Core query operations. Additionally, it supports different mocking libraries such as Moq, NSubstitute, and FakeItEasy. So if we are using any of these mocking libraries and want to test methods that implement various EF Core operations, then MockQueryable is a great choice.
Conclusion
In this article, we learned how to mock EF Core DbContext
. We used two libraries Moq.EntityFrameworkCore and MockQueryable and saw how to write unit tests using those and which one to use in which scenario etc.