web-dev-qa-db-ja.com

非同期ユニットテストでのスタビングタスクの戻りメソッド

次のクラスとそれに依存するインターフェイスがあるとします。

_public class MyController
{
    private IRepository _repository;
    public MyController(IRepository repository)
    {
        _repository = repository;
    }

    public async Task MethodUnderTest(int someId)
    {
        var o = await _repository.FindById(someId);
        // update o
        await _repository.Commit();
    }
}

public interface IRepository
{
    Task Commit();
}
_

このメソッドを単体テストすると、次のことができます(xUnitとRhino Mocksを使用)。

_[Fact]
public async Task MyTest()
{
    IRepository repositoryStub = MockRepository.GenerateStub<IRepository>();

    MyController controller = new MyController(repositoryStub);

    await controller.MethodUnderTest(1);
}
_

これはSystem.NullReferenceException:オブジェクト参照がオブジェクトのインスタンスに設定されていませんで失敗します。

次のStackTraceの場合:

_UnitTest.MyController.<MethodUnderTest>d__0.MoveNext() in 
\UnitTest\Class1.cs:line 35
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at \UnitTest\Class1.cs:line 20
_

Commit()nullを返し、非同期/待機用に生成されたステートマシンがnullMoveNext()を呼び出すため、このエラーが発生することは正しいですか?

私は次のようなことをすることでこれを修正できます:

_repositoryStub.Expect(r => r.Commit()).Return(Task.FromResult<bool>(true);
_

しかし、これは少し奇妙に感じます。_FromResult<T>_には任意のTを使用でき、テストが実行されます。ジェネリックでないFromResultを返すTaskメソッドが見つかりません。

Tに何を使用するかは重要ですか?それとも他の方法で修正する必要がありますか?

26
Wouter de Kort

何かを返す必要があります。 _async Task_メソッドはnullを返すことができません。

Tは関係ありません。必要に応じて、static readonly Task SuccessTask = Task.FromResult<object>(null);をヘルパー定数として宣言できます。 AsyncExライブラリに 類似の定数 があります。

45
Stephen Cleary

.NET Framework 4.6以降をターゲットにしている場合は、代わりに.Returns(Task.CompletedTask)を使用できますが、これは少し短いです。

4
Martin Dawson