排查单元测试中的异步方法问题:解决 FirstOrDefaultAsync() 失败

问题描述 投票:0回答:1

在我的示例代码中测试 FirstOrDefaultAsync() 时出现问题

我有下面的方法,我通过用户名获取用户 ID,并且我已经为其编写了测试。但是,使用 FirstOrDefaultAsync() 时测试不起作用。如果我切换到 FirstOrDefault(),测试工作正常。这是代码:

正在测试的方法:

public async Task<int> GetUserId(string userName)
{
   return await _unitofWork.Repository<UserEntity>().Entities
                              .Where(u => u.UserName == userName)
                              .Select(i => i.Id)
                              .FirstOrDefaultAsync();
}

测试代码:

[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);

}

问题: 这只是我为测试 FirstOrDefaultAsync() 方法而创建的示例,但它不起作用。使用 FirstOrDefault() 时测试工作正常,但使用 FirstOrDefaultAsync() 时测试失败。

支持代码: 这是我用来支持测试中的异步操作的 TestAsyncEnumerable 类:

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);
     }
 }

问题: 使用 FirstOrDefaultAsync() 时可能导致测试失败的原因是什么?我实现 TestAsyncEnumerable 类的方式或异步测试的设置方式是否有问题?

错误: 信息:  System.ArgumentException:参数表达式无效

堆栈跟踪:  IQueryProvider.Execute[TElement](表达式表达式) TestAsyncQueryProvider

1.Execute[TResult](Expression expression) line 72 TestAsyncQueryProvider
1.ExecuteAsync[TResult](表达式表达式,CancellationToken取消令牌) line 78 EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo 运算符MethodInfo, IQueryable
1 source, Expression expression, CancellationToken cancellationToken) EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable
1 源, CancellationToken CancellationToken) EntityFrameworkQueryableExtensions.FirstOrDefaultAsync[TSource](IQueryable`1 源,CancellationToken 取消令牌)

c# asp.net-core-webapi xunit
1个回答
0
投票

这要么是

TestAsyncEnumerable
中的问题,要么是在幕后使用它执行操作的 EF 代码中的问题。它特定于 ASP.NET Core 或模拟。

我将您的

TestAsyncEnumerable
代码复制/粘贴到新的控制台应用程序中,然后编写了以下最小示例:

using Microsoft.EntityFrameworkCore;

var list = new List<string> { "ab", "cde", "fgh", "ij" };

var query = new TestAsyncEnumerable<string>(list);
var first = await query.FirstOrDefaultAsync();

Console.WriteLine(first);

...并且由于同样的异常而失败。 (最初我包含了

Select
Where
子句,但不需要它们来重现该问题。)

目前还不清楚

TestAsyncEnumerable
来自哪里(无论是本土的还是来自博客文章或类似内容),但看起来这就是您需要解决的问题。

© www.soinside.com 2019 - 2024. All rights reserved.