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

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

当我运行以下代码时:

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

如果我在“

await
”之前添加“
_commands.Received().UpdateAsync
”,则会引发空引用异常。我怎样才能阻止这种情况发生,还是
await
没有必要?

c# asynchronous async-await nsubstitute
8个回答
20
投票

我在这里找到了答案。

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

11
投票
当 NSubstitute 看到异步调用时,它会自动创建一个已完成的任务,以便等待按照您在代码中期望的方式工作(并且不会抛出 NullReferenceException)。在这种情况下,这将是从您正在测试的方法内的

_commands.UpdateAsync(Status.Updated))

 返回的任务。

另一方面,

.Received()

调用正在验证是否调用了异步方法,这是完全同步的,因此不需要等待。

要记住的关键是异步方法返回

Task

。调用异步方法并返回任务是完全同步的,然后您可以等待 
Task
 来了解任务所代表的异步操作何时完成。


10
投票
根据 Stack Overflow 上的

这个答案,从 NSubstitute 版本 1.8.3 开始,您可以使用 await

,它将按预期工作,而不是抛出 NullReferenceException。

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


5
投票
显然你可以简单地

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

4
投票

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
    

0
投票
当我在“_commands.Received().UpdateAsync”之前添加“await”时, 出现错误空引用

那是因为当您不这样做时

await

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

要解决您的错误,只需在

UpdateAsync

 内放置一个断点,然后查看哪个值作为 null 传递给该方法。我怀疑 
Arg.Is<Something>
 是你的问题。


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

示例:

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

编辑

问题出在这一行:

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

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

await _service.Calculate();

您创建了一个模拟服务,但没有存根该方法。我不确定如何在 Nunit 中执行此操作(我们主要使用 Rhino,我需要检查),但您需要存根计算方法以返回空任务 (Task.FromResult(0))。默认情况下,存根方法返回默认返回类型,并且 default(Task) 为 null。

关于

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


0
投票
我在 NSubsittute 1.8.3 中观察到此问题,似乎已在 1.9.0 版本或更高版本中修复。

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