I am trying to make a FakeDbContext with a FakeDbSet for unit testing.
But I get the following error (see below). I am extending DbSet so normally IDbAsyncEnumerable
Just a note for people using the boilerplate code discussed above from Microsoft, here is a quick helper class that can transform your mocked up data into the async result. Just add to the bottom of the MS code and call with something like
var fakeDateAsMockAsyncQueryResult = new MockAsyncData<SomeType>().MockAsyncQueryResult(fakeDataList.AsQueryable());
.......
internal class MockAsyncData<T> where T : class
{
public Mock<DbSet<T>> MockAsyncQueryResult(IQueryable<T> data)
{
var mockSet = new Mock<DbSet<T>>();
mockSet.As<IDbAsyncEnumerable<T>>()
.Setup(m => m.GetAsyncEnumerator())
.Returns(new TestDbAsyncEnumerator<T>(data.GetEnumerator()));
mockSet.As<IQueryable<T>>()
.Setup(m => m.Provider)
.Returns(new TestDbAsyncQueryProvider<T>(data.Provider));
mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
return mockSet;
}
}
It's the same code as in the MS example but generic and reusable from many different unit tests.
In my case the exception was caused by using the wrong ToListAsync
extension.
It came from:
using System.Data.Entity;
instead of
using Microsoft.EntityFrameworkCore;
Changing the namespace fixed the error.
Your scenario is explicitly mentioned in the link provided with the exception message (http://go.microsoft.com/fwlink/?LinkId=287068). The missing ingredient is the IDbAsyncQueryProvider that you should return from your Provider property.
Just navigate through the link to arrive at the boilerplate implementation.
Little I can add, I'll just quote the essential phrase:
In order to use asynchronous queries we need to do a little more work. If we tried to use our Moq DbSet with the GetAllBlogsAsync method we would get the following exception:
System.InvalidOperationException: The source IQueryable doesn't implement IDbAsyncEnumerable. Only sources that implement IDbAsyncEnumerable can be used for Entity Framework asynchronous operations. For more details see http://go.microsoft.com/fwlink/?LinkId=287068.
In order to use the async methods we need to create an in-memory DbAsyncQueryProvider to process the async query. Whilst it would be possible to setup a query provider using Moq, it is much easier to create a test double implementation in code. The code for this implementation is as follows:
etc...
Not directly related but might help some others. If you're using DelegateCompiler, make sure you're using DecompileAsync
rather than Decompile
in the EF packages (source).
DbSet
probably implements IDbSet
implicitly so that those methods are not available for interface mapping in your derived class.
Don't derive from IDbSet<TEntity>
.
You cannot call the explicitly implemented interface members of IDbSet from a re-implementation of that interface.
To solved my IDbAsyncEnumerable issue:
Changed my project's target from .NetFramework 4.0 to .NetFramework 4.5
Reinstalled EntityFramework 6.1.3 Nuget package.
At this point, my Visual Studio's IDE Show Potencial Fixes advisor, allowed me to reference System.Data.Entity.Infrastructure namespace
using System.Data.Entity.Infrastructure;