I am trying to write unit test by mocking. My test is mocking the GetAll
method which is using FirstOrDefaultAsync
method. If I change FirstOrDefaultAsync
to FirstOrDefault
my test works fine but it keeps failing when I put back FirstOrDefaultAsync
.
My unit test code is below
[TestMethod]
public async Task GetAsync_WithUser_ReturnsOk()
{
var data = GetUserResult();
var mockContext = Substitute.For<IPostAnythingContext>();
var mockSet = data.GenerateMockDbSetForAsync();
mockContext.Users.Returns(mockSet);
uow.User.GetAll().Returns(data);
var result = await sut.GetAsync("a@a.com");
}
Code I am trying to test
public async Task<Result<Users>> GetAsync(string email)
{
var result = Uow.User.GetAll()
.Where(x => x.EmailAddress == email)
.Include(x => x.UsersUsersTypes);
return Result.Ok(await result.FirstOrDefaultAsync());
}
My extension method
public static DbSet<TEntity> GenerateMockDbSetForAsync<TEntity>(this IEnumerable<TEntity> queryableEnumerable) where TEntity : class
{
var queryable = queryableEnumerable as IQueryable<TEntity> ?? queryableEnumerable.AsQueryable();
var mockSet = Substitute.For<DbSet<TEntity>, IQueryable<TEntity>, IDbAsyncEnumerable<TEntity>>();
// async support
var castMockSet = (IQueryable<TEntity>)mockSet;
var castAsyncEnum = (IDbAsyncEnumerable<TEntity>)mockSet;
castAsyncEnum.GetAsyncEnumerator().Returns(new TestDbAsyncEnumerator<TEntity>(queryable.GetEnumerator()));
castMockSet.Provider.Returns(new TestDbAsyncQueryProvider<TEntity>(queryable.Provider));
castMockSet.Expression.Returns(queryable.Expression);
castMockSet.ElementType.Returns(queryable.ElementType);
castMockSet.GetEnumerator().Returns(queryable.GetEnumerator());
return mockSet;
}
Exception I am getting
Test Name: GetAsync_WithUser_ReturnsOk
Test FullName: PostAnything.Business.Tests.UsersServiceTests.GetAsync_WithUser_ReturnsOk
Test Source: C:\TFS\PostAnything\PostAnything.Business.Tests\UsersServiceTests.cs : line 40
Test Outcome: Failed
Test Duration: 0:00:02.5118084
Result StackTrace:
at System.Data.Entity.QueryableExtensions.FirstOrDefaultAsyncTSource
at System.Data.Entity.QueryableExtensions.FirstOrDefaultAsyncTSource
at PostAnything.Business.Implementation.UsersService.d__2.MoveNext() in C:\TFS\PostAnything\PostAnything.Business\Implementation\UsersService.cs:line 28
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at PostAnything.Business.Tests.UsersServiceTests.d__5.MoveNext() in C:\TFS\PostAnything\PostAnything.Business.Tests\UsersServiceTests.cs:line 49
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
Result Message:
Test method PostAnything.Business.Tests.UsersServiceTests.GetAsync_WithUser_ReturnsOk threw exception:
System.InvalidOperationException: The provider for the source IQueryable doesn't implement IDbAsyncQueryProvider. Only providers that implement IDbAsyncQueryProvider can be used for Entity Framework asynchronous operations. For more details see http://go.microsoft.com/fwlink/?LinkId=287068.
In order to mock asynchronous queries you need to implement IDbAsyncQueryProvider
interface. I don't know how do you implement GenerateMockDbSetForAsync
method but here is complete toturial on mocking async methods.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.