Hi Coders,
Have you ever faced problems mocking asynchronous methods that EF core provides?
Have you ever faced a problem mocking the asynchronous repository?
Have you got the error “The provider for the source IQueryable doesn`t implement IAsyncQueryProvider. Only providers that implement IEntityQueryProvider can be used for Entity Framework asynchronous operations.”
Well, You guys got the perfect link to implement mocking the asynchronous methods and increase your code coverage
EF6 came up with new methods for you to execute the query asynchronously. Such as FirstAsync, ToListAsync , ForEachAsync, etc.
When you use them in your repo call, you find it difficult to mock them. Because you`ll be running the tests against in-memory test double of a DbSet.
So, what do we do? We need to create an in-memory DbAsyncQueryProvider to process the async query.
Enough Theory? Let’s look at the code
using System.Collections.Generic;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
namespace TestingMockAsyncMethods
{
internal class TestDbAsyncQueryProvider<TEntity> : IDbAsyncQueryProvider
{
private readonly IQueryProvider _inner;
internal TestDbAsyncQueryProvider(IQueryProvider inner)
{
_inner = inner;
}
public IQueryable CreateQuery(Expression expression)
{
return new TestDbAsyncEnumerable<TEntity>(expression);
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new TestDbAsyncEnumerable<TElement>(expression);
}
public object Execute(Expression expression)
{
return _inner.Execute(expression);
}
public TResult Execute<TResult>(Expression expression)
{
return _inner.Execute<TResult>(expression);
}
public Task<object> ExecuteAsync(Expression expression, CancellationToken cancellationToken)
{
return Task.FromResult(Execute(expression));
}
public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
{
return Task.FromResult(Execute<TResult>(expression));
}
}
internal class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>, IQueryable<T>
{
public TestDbAsyncEnumerable(IEnumerable<T> enumerable)
: base(enumerable)
{ }
public TestDbAsyncEnumerable(Expression expression)
: base(expression)
{ }
public IDbAsyncEnumerator<T> GetAsyncEnumerator()
{
return new TestDbAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
}
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
{
return GetAsyncEnumerator();
}
IQueryProvider IQueryable.Provider
{
get { return new TestDbAsyncQueryProvider<T>(this); }
}
}
internal class TestDbAsyncEnumerator<T> : IDbAsyncEnumerator<T>
{
private readonly IEnumerator<T> _inner;
public TestDbAsyncEnumerator(IEnumerator<T> inner)
{
_inner = inner;
}
public void Dispose()
{
_inner.Dispose();
}
public Task<bool> MoveNextAsync(CancellationToken cancellationToken)
{
return Task.FromResult(_inner.MoveNext());
}
public T Current
{
get { return _inner.Current; }
}
object IDbAsyncEnumerator.Current
{
get { return Current; }
}
}
}
So now, we have an exact same replica of a DbAsyncQueryProvider.
Tip: Remember to make these classes internal so that they`re only applicable for the test project.
Well, you`ve created your own replica of in memory DbAsyncQueryProvider. Let’s see how to use them in our unit tests.
Let’s consider we have an Employee table and we use GetAllEmployeesAsync implemented as below.
public class EmployeeRepository : IEmployeeRepository
{
public async Task<List<Employees>> GetAllEmployeesAsync()
{
return await _dbContext.Employees.ToListAsync();
}
}
Let’s see how our Test case looks like
[TestClass]
public class EmployeeRepositoryTests
{
[TestMethod]
public async Task GetAllEmployeesAsync_ReturnsEmployeeData()
{
var repository = new EmployeeRepository(_mockDbContext.Object);
var employees = await repository.GetAllEmployeesAsync();
Assert.AreEqual(3, employees.Count);
Assert.AreEqual("AAA", Employeess[0].Name);
Assert.AreEqual("BBB", Employeess[1].Name);
Assert.AreEqual("ZZZ", Employeess[2].Name);
}
}
So, when we run, we get the below error
“The provider for the source IQueryable doesn`t implement IAsyncQueryProvider. Only providers that implement IEntityQueryProvider can be used for Entity Framework asynchronous operations.”
So, let’s add some code in our test case so we can mock the IAsyncQueryProvider with our own TestDbAsyncQueryProvider.
[Example to mock Async Query Provider]
[TestClass]
public class EmployeeRepositoryTests
{
[TestMethod]
public async Task GetAllEmployeesAsync_ReturnsEmployeeData()
{
var mockEmployeeDbSet = new Mock<DbSet<Employees>>();
mockEmployeeDbSet .As<IDbAsyncEnumerable<Employees>>()
.Setup(m => m.GetAsyncEnumerator())
.Returns(new TestDbAsyncEnumerator<Employees>(data.GetEnumerator()));
mockEmployeeDbSet .As<IQueryable<Employees>>()
.Setup(m => m.Provider)
.Returns(new TestDbAsyncQueryProvider<Employees>(data.Provider));
mockEmployeeDbSet .As<IQueryable<Employees>>().Setup(m => m.Expression).Returns(data.Expression);
mockEmployeeDbSet .As<IQueryable<Employees>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockEmployeeDbSet .As<IQueryable<Employees>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
var mockContext = new Mock<EmployeesgingContext>();
mockContext.Setup(c => c.Employeess).Returns(mockEmployeeDbSet .Object);
var service = new EmployeesService(mockContext.Object);
var Employeess = await service.GetAllEmployeessAsync();
Assert.AreEqual(3, Employeess.Count);
Assert.AreEqual("AAA", Employeess[0].Name);
Assert.AreEqual("BBB", Employeess[1].Name);
Assert.AreEqual("ZZZ", Employeess[2].Name);
}
}
Tip: Mock these in Constructor so that you don`t need to mock them in every test method you write
Here TestDbAsyncQueryProvider works as an async query provider. Now the test marks itself green and your code is covered with unit tests.
Happy coding


