简体   繁体   English

Moq中从未调用过的模拟异步方法

[英]Mocked async method never called in Moq

I have a database repository interface: 我有一个数据库存储库接口:

public interface IDbRepository
{
  Task<T> GetDataAsync<T>(Func<T, bool> filter = null) where T : class;
}

I have the following fixture: 我有以下装置:

private List<Status> statusList = new List<Status> {
      new Status { Name="Status A"},
      new Status { Name="Status B"}
    };

var repositoryMock = new Mock<IDbRepository>();
Func<Status, bool> filter = It.IsAny<Func<Status, bool>>();
repositoryMock.Setup(repository => repository.GetDataAsync(filter))
  .ReturnsAsync(
  () =>
  {
    //NEVER CALLED
    if (filter == null)
      return statusList.FirstOrDefault();
    return statusList.FirstOrDefault(filter);
  });

I use the following test: 我使用以下测试:

[Fact]
public async Task Repository_GetDataAsync_Filter()
{
  var repository = repositoryTestFixture.Ioc.Resolve<IDbRepository>();
  Func<Status, bool> filter = stat => stat.Name == "Status A";
  var res = await repository.GetDataAsync(filter);
  //await task1.ContinueWith(ctask => Assert.NotNull(ctask.Result));
  Assert.NotNull(res); //ALWAYS NULL!!!
}

I tried to debug, and the method never called so I always get a failed test. 我尝试调试,但是该方法从未调用过,因此我总是无法通过测试。

According to @Stephen Cleary, the test isn't required to call Wait() or Result , and the old official way is deprecated as of Visual Studio 2015, async tests are supported out of the box. 根据@Stephen Cleary的说法,不需要调用 Wait()Result ,并且从Visual Studio 2015开始不赞成使用旧的官方方法 ,即开即用地支持异步测试。

Stephen Cleary • https://stephencleary.com/ 11 months ago (2017-11-03 12:22:06 pm) As noted in the big red banner at the top of the page, this solution was designed for VS2010. Stephen Cleary• https ://stephencleary.com/ 11个月前(2017-11-03 12:22:06 pm)如页面顶部的大红色横幅所示,此解决方案是为VS2010设计的。 VS2015 works fine with async Task unit tests out of the box. VS2015在开箱即用的异步任务单元测试中运行良好。

What should I change? 我应该改变什么?

The use of the It.IsAny<>() is inaccurate. It.IsAny<>()的使用不正确。 It was meant to be used directly in the Setup expression only. 它只能直接在Setup表达式中使用。 Not in variables. 不在变量中。

When arguments do not match the mock is not invoked. 当参数不匹配时,不调用模拟。 Hence you get null. 因此,您得到空值。

Access passed argument within the ReturnAsync delegate 访问ReturnAsync委托中传递的参数

var repositoryMock = new Mock<IDbRepository>();
repositoryMock
    .Setup(repository => repository.GetDataAsync(It.IsAny<Func<Status, bool>>()))
    .ReturnsAsync((Func<Status, bool> filter) => {//<-- grab passed argument here
        if (filter == null)
            return statusList.FirstOrDefault();
        return statusList.FirstOrDefault(filter);
    });

I think the problem here is due to your filter local variable. 我认为这里的问题是由于您的filter局部变量引起的。 By creating a variable that is not an expression, you are actually executing the It.IsAny call and returning null. 通过创建不是表达式的变量,您实际上是在执行It.IsAny调用并返回null。 As you can imagine, null will only match a null func, and this is why you don't see the ReturnsAsync being hit. 可以想象, null只会匹配null函数,这就是为什么看不到ReturnsAsync原因。

Please try inlining that variable or changing its type to Expression<Func<Status, bool>> and see if that works as expected. 请尝试内联该变量或将其类型更改为Expression<Func<Status, bool>>然后查看它是否按预期工作。

You have to always keep in mind that mock setup is done using expressions, so usually the It.IsX calls are never executed per se, just analysed and compared as an expression tree. 您必须始终牢记,模拟设置是使用表达式完成的,因此通常It.IsX调用本身不会执行,只是作为表达式树进行分析和比较。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM