'Moq mocking EF DbContext
I have a Repository pattern that interacts with Entity Framework. I'd like to run some unit tests on the repository, and for this reason, I would like to mock DbContext.
So I've created a unit test project (.Net Core 3.1), using Moq as package for unit testing, everything seems to be ok, but when I perform a .ToListAsync() on my repository it throws the following exception:
System.NotImplementedException : The method or operation is not implemented. Stack Trace: IAsyncEnumerable.GetAsyncEnumerator(CancellationToken cancellationToken) ConfiguredCancelableAsyncEnumerable
1.GetAsyncEnumerator() EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable1 source, CancellationToken cancellationToken)
The source code:
public class Customer
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class CustomersDbContext : DbContext
{
public virtual DbSet<Customer> Customers { get; set; }
public CustomersDbContext(DbContextOptions<Customer> options) : base(options) { }
}
public interface ICustomerRepository
{
Task<IEnumerable<Customer>> GetCustomersAsync(Guid? customerId);
}
public class CustomerRepository : ICustomerRepository
{
private readonly CustomersDbContext _dbContext;
public CustomerRepository(CustomersDbContext dbContext)
{
_dbContext = dbContext;
_dbContext.Database.EnsureCreated();
}
public async Task<IEnumerable<Customer>> GetCustomersAsync(Guid? customerId)
{
IEnumerable<Customer> customers = null;
if (customerId.HasValue)
{
var customer = await _dbContext.Customers.FindAsync(new object[] { customerId.Value }, CancellationToken.None);
if (customer != null)
customers = new List<Customer>() { customer };
}
else
{
customers = await _dbContext.Customers.ToListAsync(CancellationToken.None);
}
return customers;
}
}
public class CustomerServiceUnitTests
{
private Mock<CustomersDbContext> GetCustomerDbContextMock()
{
var data = new List<Customer>()
{
new Customer()
{
Id = Guid.NewGuid(),
Name = "Name 1"
},
new Customer()
{
Id = Guid.NewGuid(),
Name = "Name 2"
}
}.AsQueryable();
var mockSet = new Mock<DbSet<Customer>>();
mockSet.As<IQueryable<Customer>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Customer>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Customer>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Customer>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
var optionsBuilder = new DbContextOptions<CustomersDbContext>();
var mockContext = new Mock<CustomersDbContext>(optionsBuilder);
Mock<DatabaseFacade> databaseFacade = new Mock<DatabaseFacade>(mockContext.Object);
databaseFacade.Setup(d => d.EnsureCreatedAsync(CancellationToken.None)).Returns(Task.FromResult(true));
mockContext.Setup(c => c.Database).Returns(databaseFacade.Object);
mockContext.Setup(c => c.Customers).Returns(mockSet.Object);
return mockContext;
}
[Fact]
public async Task Infrastructure_CustomerRepository_GetAll()
{
var mockContext = this.GetCustomerDbContextMock();
ICustomerRepository customerRepository = new CustomerRepository(mockContext.Object);
var customers = await customerRepository.GetCustomersAsync(null);
Assert.NotNull(customers);
Assert.Equal(2, customers.Count());
}
}
If I send an ID filled to the repository it works fine, so this seems to be not ok only for .ToListAsync().
I'm kinda stuck here, what can I do to overcome this?
Solution 1:[1]
In order to execute Asynchronous read operation (ToListAsync()) you need to mock an additional interface called "IDBAsyncQueryProvider".
Here's is the required link you can follow. It is under the heading "Testing with async queries"
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Kashish Sachdeva |
