检查异步方法的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.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值之前结束,这意味着测试结束。你有比赛条件。当您在UpdateAsync
上await
时,该方法实际上异步地等待操作完成,并且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。我想你会想等待它的执行。