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

时间:2015-06-23 07:06:06

标签: c# asynchronous async-await nsubstitute

当我运行以下代码时:

[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没有必要?

7 个答案:

答案 0 :(得分:16)

我找到了答案here

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

答案 1 :(得分:6)

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

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

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

答案 2 :(得分:5)

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

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

答案 3 :(得分:2)

Jake Ginnivan answer正确指出不需要Received await,但编译器不理解它并显示

  

警告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

答案 4 :(得分:1)

显然您可以简单地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)); 
}

答案 5 :(得分:0)

  

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

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

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

答案 6 :(得分:0)

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

示例:

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

修改

问题出在这一行:

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

或更准确地说,当你称之为方法时:

await _service.Calculate();

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

关于your gist:DoSomethingAsync不应该是异步无效的。我想你会想要等待它的执行。