web-dev-qa-db-ja.com

非同期メソッドのReceived()呼び出しを確認します

次のコードを実行すると:

_[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」を追加すると、null参照例外がスローされます。どうすればこれを止めることができますか、またはawaitは必要ありませんか?

19
letitbe

私は答えを見つけました ここ

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

NSubstituteが非同期呼び出しを検出すると、完了したタスクが自動的に作成されるため、コードで期待どおりに待機が機能します(NullReferenceExceptionはスローされません)。この場合、それはテストしているメソッド内の_commands.UpdateAsync(Status.Updated))から返されるタスクになります。

一方、.Received()呼び出しは、asyncメソッドが呼び出されたこと、つまり完全に同期していることを確認しているため、待機する必要はありません。

覚えておくべき重要なことは、非同期メソッドはTaskを返すということです。 asyncメソッドを呼び出してタスクを返すのは完全に同期しているので、タスクが表す非同期操作がいつ完了するかを知るためにTaskを待ちます。

6
Jake Ginnivan

スタックオーバーフローの この回答 によると、NSubstituteバージョン1.8.3以降、awaitを使用でき、NullReferenceExceptionをスローするのではなく、期待どおりに機能します。

バージョン1.5.0で試してみたところ、説明どおりにNullReferenceExceptionが発生しましたが、現在は最新(1.10.0)を使用しており、正常に機能しています。

5
pipedreambomb

Jake Ginnivanの回答 は、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
2

UpdateAsyncがスタブメソッドである場合は、nullではなく空のタスクを返す必要があります。 nullタスクを待つことはできません。

例:

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

編集

問題はこの行にあります:

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

もっと正確に言えば、それをメソッドと呼ぶとき:

await _service.Calculate();

モックサービスを作成しますが、メソッドをスタブ化しません。 Nunitでそれを行う方法はわかりませんが(主にRhinoを使用しているので、確認する必要があります)、空のタスク(Task.FromResult(0))を返すにはCalculateメソッドをスタブ化する必要があります。デフォルトでは、スタブメソッドはデフォルトの戻り値の型を返し、default(Task)はnullです。

について あなたの要点 :DoSomethingAsyncはasyncvoidであってはなりません。私はあなたがそれの実行を待ちたいと思うだろうと思います。

0
Andrei Tătar

「_commands.Received()。UpdateAsync」の前に「await」を追加すると、エラーnull参照が発生します

これは、awaitを実行しない場合、メソッド(Can_Test_Update)メソッドに渡したnull値を実際にチェックする前に終了する場合があります。つまり、テストが終了します。競合状態があります。 awaitUpdateAsyncを実行すると、メソッドは実際には操作が完了するのを非同期的に待機し、UpdateAsyncは渡されたnullにアクセスする機会を得ます。

エラーを解決するには、UpdateAsync内にブレークポイントを設定し、どの値がnullとしてメソッドに渡されるかを確認します。 Arg.Is<Something>はあなたの問題です。

0
Yuval Itzchakov