检查异步方法的调用 Received()
Check calls Received() for async method
当我运行以下代码时:
[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
没有必要?
when i add "await" preceding the "_commands.Received().UpdateAsync",
it occurs error null reference
那是因为当您不 await
时,方法 (Can_Test_Update
) 可能会在它实际检查您传递给该方法的空值之前结束,这意味着测试结束。你有一个竞争条件。当您在 UpdateAsync
上 await
时,该方法实际上异步等待操作完成,并且 UpdateAsync
有机会访问您传递给它的 null。
要解决您的错误,只需在 UpdateAsync
内放置一个断点,然后查看哪个值作为 null 传递给该方法。我怀疑 Arg.Is<Something>
是你的问题。
如果 UpdateAsync 是存根方法,您需要 return 一个空任务,而不是 null。您不能等待空任务。
示例:
receivedObject.Stub(s => s.Update(...)).Return(Task.FromResult(0));
编辑
问题出在这一行:
var mockService = Substitute.For<ICalculationServiceAsync>();
或者更准确地说,当您调用它的方法时:
await _service.Calculate();
您创建了一个模拟服务,但没有对方法进行存根。我不确定如何在 Nunit 中执行此操作(我们主要使用 Rhino,我需要检查一下),但您需要将计算方法存入 return 一个空任务 (Task.FromResult(0 )).默认情况下,存根方法 return 默认 return 类型和默认(任务)为空。
关于 your gist:DoSomethingAsync 不应该是 async void。我假设你想等待它的执行。
我找到了答案 here。
Received.InOrder(async () =>
{
await _Commands.UpdateAsync(Arg.Is<Lobby>(l => l.Status == Status.Updated));
});
当 NSubstitute 看到一个异步调用时,它会自动创建一个已完成的任务,以便 await 按照您在代码中预期的方式工作(并且不会抛出 NullReferenceException)。在这种情况下,这将是您正在测试的方法中从 _commands.UpdateAsync(Status.Updated))
编辑的任务 return。
另一方面,.Received()
调用正在验证是否调用了异步方法,它是完全同步的,因此不需要等待。
要记住的关键是异步方法 return 和 Task
。调用异步方法和 return 执行任务是完全同步的,然后等待 Task
知道任务代表的异步操作何时完成。
根据 Stack Overflow 上的 ,从 NSubstitute 版本 1.8.3 开始,您可以使用 await
,它将按预期工作,而不是抛出 NullReferenceException。
我刚刚试用了 1.5.0 版本并得到了你描述的 NullReferenceException,但现在我使用的是最新版本 (1.10.0),它运行良好。
正确指出 Received 不需要等待,但是编译器不理解它并显示
warning CS4014: Because this call is not awaited, execution of the
current method continues before the call is completed. Consider
applying the 'await' operator to the result of the call.
最简单的解决方法是取消警告
#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
显然你可以简单地 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));
}
当我运行以下代码时:
[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
没有必要?
when i add "await" preceding the "_commands.Received().UpdateAsync", it occurs error null reference
那是因为当您不 await
时,方法 (Can_Test_Update
) 可能会在它实际检查您传递给该方法的空值之前结束,这意味着测试结束。你有一个竞争条件。当您在 UpdateAsync
上 await
时,该方法实际上异步等待操作完成,并且 UpdateAsync
有机会访问您传递给它的 null。
要解决您的错误,只需在 UpdateAsync
内放置一个断点,然后查看哪个值作为 null 传递给该方法。我怀疑 Arg.Is<Something>
是你的问题。
如果 UpdateAsync 是存根方法,您需要 return 一个空任务,而不是 null。您不能等待空任务。
示例:
receivedObject.Stub(s => s.Update(...)).Return(Task.FromResult(0));
编辑
问题出在这一行:
var mockService = Substitute.For<ICalculationServiceAsync>();
或者更准确地说,当您调用它的方法时:
await _service.Calculate();
您创建了一个模拟服务,但没有对方法进行存根。我不确定如何在 Nunit 中执行此操作(我们主要使用 Rhino,我需要检查一下),但您需要将计算方法存入 return 一个空任务 (Task.FromResult(0 )).默认情况下,存根方法 return 默认 return 类型和默认(任务)为空。
关于 your gist:DoSomethingAsync 不应该是 async void。我假设你想等待它的执行。
我找到了答案 here。
Received.InOrder(async () =>
{
await _Commands.UpdateAsync(Arg.Is<Lobby>(l => l.Status == Status.Updated));
});
当 NSubstitute 看到一个异步调用时,它会自动创建一个已完成的任务,以便 await 按照您在代码中预期的方式工作(并且不会抛出 NullReferenceException)。在这种情况下,这将是您正在测试的方法中从 _commands.UpdateAsync(Status.Updated))
编辑的任务 return。
另一方面,.Received()
调用正在验证是否调用了异步方法,它是完全同步的,因此不需要等待。
要记住的关键是异步方法 return 和 Task
。调用异步方法和 return 执行任务是完全同步的,然后等待 Task
知道任务代表的异步操作何时完成。
根据 Stack Overflow 上的 await
,它将按预期工作,而不是抛出 NullReferenceException。
我刚刚试用了 1.5.0 版本并得到了你描述的 NullReferenceException,但现在我使用的是最新版本 (1.10.0),它运行良好。
warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
最简单的解决方法是取消警告
#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
显然你可以简单地 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));
}