Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stubbing Task returning method in async unit test

Let's say I have the following class and an interface it depends on:

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

When I unit test this method I can do the following (using xUnit and Rhino Mocks):

[Fact] public async Task MyTest() {     IRepository repositoryStub = MockRepository.GenerateStub<IRepository>();      MyController controller = new MyController(repositoryStub);      await controller.MethodUnderTest(1); } 

This fails with a System.NullReferenceException : Object reference not set to an instance of an object.

With the following 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 

Is it correct that this error occurs because the Commit() returns null and the statemachine generated for the async/await calls MoveNext() on a null?

I can fix this by doing something like:

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

But this feels a little strange.. I can use any T for FromResult<T> and the test will run. I can't find a FromResult method that will return a non-generic Task.

Does it matter what I use for T? Or should I fix this some other way?

like image 919
Wouter de Kort Avatar asked Dec 21 '12 14:12

Wouter de Kort


1 Answers

You will need to return something; async Task methods cannot ever return null.

The T doesn't matter. You could declare a static readonly Task SuccessTask = Task.FromResult<object>(null); as a helper constant if you want. I have similar constants in my AsyncEx library.

like image 145
Stephen Cleary Avatar answered Sep 19 '22 13:09

Stephen Cleary