Problem with Testing FirstOrDefaultAsync() in My Example Code
I have the method below where I'm fetching the user ID by username, and I've written tests for it. However, the tests are not working when using FirstOrDefaultAsync(). If I switch to FirstOrDefault(), the tests work fine. Here's the code:
Method Being Tested:
public async Task<int> GetUserId(string userName)
{
return await _unitofWork.Repository<UserEntity>().Entities
.Where(u => u.UserName == userName)
.Select(i => i.Id)
.FirstOrDefaultAsync();
}
Test Code:
[Fact]
public async Task GetUserIdTests()
{
//Arrange
var userEntity= new List<UserEntity>
{
new UserEntity
{
UserName = "testuser", Id = 1
}
};
var repositoryMock = new Mock<IGenericRepository<UserEntity>>();
repositoryMock.Setup(r => r.Entities).Returns(new TestAsyncEnumerable<UserEntity>(systemUsers).AsQueryable());
_unitOfWorkMock.Setup(u => u.Repository<UserEntity>()).Returns(repositoryMock.Object);
var service= new service(_userContextMock.Object,_vaultServiceMock.Object);
//Act
var result = await service.GetUserId("testuser");
//Assert
Assert.NotEqual(0, result);
}
Issue:
This is just an example I created to test the FirstOrDefaultAsync() method, but it is not working. The test works correctly when using FirstOrDefault() but fails with FirstOrDefaultAsync().
Supporting Code:
Here is the TestAsyncEnumerable class that I'm using to support async operations in the test:
public class TestAsyncEnumerable<T> : EnumerableQuery<T>, IAsyncEnumerable<T>, IQueryable<T>
{
public TestAsyncEnumerable(IEnumerable<T> enumerable) : base(enumerable) { }
public TestAsyncEnumerable(Expression expression) : base(expression) { }
public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new TestAsyncEnumerator<T>(this.AsEnumerable().GetEnumerator());
}
IAsyncEnumerator<T> IAsyncEnumerable<T>.GetAsyncEnumerator(CancellationToken cancellationToken)
{
return GetAsyncEnumerator(cancellationToken);
}
IQueryProvider IQueryable.Provider => new TestAsyncQueryProvider<T>(this);
}
public class TestAsyncEnumerator<T> : IAsyncEnumerator<T>
{
private readonly IEnumerator<T> _inner;
public TestAsyncEnumerator(IEnumerator<T> inner)
{
_inner = inner;
}
public ValueTask DisposeAsync()
{
_inner.Dispose();
return ValueTask.CompletedTask;
}
public ValueTask<bool> MoveNextAsync()
{
return new ValueTask<bool>(_inner.MoveNext());
}
public T Current => _inner.Current;
}
public class TestAsyncQueryProvider<T> : IAsyncQueryProvider
{
private readonly IQueryProvider _inner;
public TestAsyncQueryProvider(IQueryProvider inner)
{
_inner = inner;
}
public IQueryable CreateQuery(Expression expression)
{
return new TestAsyncEnumerable<T>(expression);
}
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
return new TestAsyncEnumerable<TElement>(expression);
}
public object Execute(Expression expression)
{
return _inner.Execute(expression);
}
public TResult Execute<TResult>(Expression expression)
{
return _inner.Execute<TResult>(expression);
}
public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
{
return Execute<TResult>(expression);
}
public IAsyncEnumerable<TResult> ExecuteAsync<TResult>(Expression expression)
{
return new TestAsyncEnumerable<TResult>(expression);
}
}
Question:
What might be causing the test to fail when using FirstOrDefaultAsync()? Is there something wrong with how I've implemented the TestAsyncEnumerable class or how the async test is set up?
**Error:**
Message:
System.ArgumentException : Argument expression is not valid
Stack Trace:
IQueryProvider.Execute[TElement](Expression expression)
TestAsyncQueryProvider`1.Execute[TResult](Expression expression) line 72
TestAsyncQueryProvider`1.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken) line 78
EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, Expression expression, CancellationToken cancellationToken)
EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, CancellationToken cancellationToken)
EntityFrameworkQueryableExtensions.FirstOrDefaultAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
TestAsyncEnumerablecome from? (And what isSystemUservsUserEntity?) It would be really helpful if you'd provide a minimal reproducible example.Task<T>or similar? There's a difference between an asynchronous method, and a property returning an "asynchronously-consumable" collection. I may be missing something, of course.