检查异步方法的Received()调用

本文关键字:调用 Received 异步方法 检查 | 更新日期: 2023-09-27 17:59:10

当我运行以下代码时:

[Test]
public async Task Can_Test_Update()
{
    var response = await _controller.UpdateAsync(Guid.NewGuid());
    response.Valid.Should().BeTrue();
    _commands.Received().UpdateAsync(
        Arg.Is<Something>(
            l => l.Status == Status.Updated)); 
}

如果我在"_commands.Received().UpdateAsync"之前添加"await",它将抛出一个空引用异常。我如何才能阻止这种情况的发生,或者await不是必需的?

检查异步方法的Received()调用

我在这里找到了答案。

Received.InOrder(async () =>
{
    await _Commands.UpdateAsync(Arg.Is<Lobby>(l => l.Status == Status.Updated));
});

当NSubstitute看到异步调用时,它会自动创建一个已完成的任务,以便等待按照您在代码中的预期工作(而不是抛出NullReferenceException)。在这种情况下,这将是从正在测试的方法中的_commands.UpdateAsync(Status.Updated))返回的任务。

另一方面,.Received()调用正在验证是否调用了异步方法,该方法是完全同步的,因此不需要等待。

需要记住的关键是异步方法返回一个Task。调用async方法并返回任务是完全同步的,然后等待Task来知道任务所代表的异步操作何时完成。

根据Stack Overflow上的这个答案,从NSubstitute 1.8.3版本开始,您可以使用await,它将按预期工作,而不是抛出NullReferenceException。

正如你所描述的,我刚刚在1.5.0版本上尝试过它,并得到了NullReferenceException,但现在我在最新版本(1.10.0)上,它运行得很好。

显然,您可以简单地await Received方法:

[Test]
public async Task Can_Test_Update()
{
    var response = await _controller.UpdateAsync(Guid.NewGuid());
    response.Valid.Should().BeTrue();
    await _commands.Received().UpdateAsync(
        Arg.Is<Something>(
            l => l.Status == Status.Updated)); 
}

Jake Ginnivan的回答正确地指出,不需要等待接收,但编译器不理解,并显示

警告CS4014:由于未等待此调用当前方法在调用完成之前继续。考虑将'await'运算符应用于调用的结果。

最简单的解决方法是抑制警告

 #pragma warning disable 4014 //for .Received await is not required, so suppress warning “Consider applying the 'await' operator”
   _publisher.Received(totalNumber).MyMethod(Arg.Any<ParamType>());
 #pragma warning restore 4014

当我在"_commands.Received().UpdateAsync"之前添加"wait"时,出现错误空引用

这是因为当您没有await时,方法(Can_Test_Update)可能会在实际检查您传递给该方法的null值之前结束,这意味着测试结束。你有比赛条件。当您在UpdateAsyncawait时,该方法实际上异步地等待操作完成,并且UpdateAsync有机会访问您传递给它的null。

要解决错误,只需在UpdateAsync中放置一个断点,然后查看哪个值作为null传递给该方法。我怀疑Arg.Is<Something>是你的问题。

如果UpdateAsync是一个存根方法,则需要返回一个空Task,而不是null。不能等待空任务。

示例:

receivedObject.Stub(s => s.Update(...)).Return(Task.FromResult(0));

编辑

问题出在这一行:

var mockService = Substitute.For<ICalculationServiceAsync>(); 

或者更确切地说,当你调用它的方法时:

await _service.Calculate();

您创建了一个模拟服务,但没有存根该方法。我不知道如何在Nunit中完成它(我们主要使用Rhino,我需要检查一下),但你需要存根你的Calculate方法来返回一个空的Task(Task.FromResult(0))。默认情况下,存根方法返回默认的返回类型,默认值(Task)为null。

关于要点:DoSomethingAsync不应该是async void。我想你会想等待它的执行。