Mocking (stubbing) async calls (async/await)
Published by Carlos Blé on 10/02/2015
.Net 4.5 came out with a really handy built-in asynchronous mechanism, async and await. However the method signature of a void method is a bit strange:
public async Task SomeAction(){...} |
It is possible to use async void but it’s not recommended unless we are talking about event handlers. It’s also a bit strange the fact that the type specified in the signature is not the one returned:
public async Task<string> SomeQuery(){ | |
..... | |
return someString; | |
} |
But everything else is great and very handy.
Stubbing method calls can be hard though. You can get weird exceptions like System.AggregateException when running tests with NUnit. The problem comes up when awaiting for a
stubbed async method:
// notice that async tests with NUnit require the method to be async Task rather than void | |
[Test] public async Task | |
filter_results_from_search(){ | |
var srv = new Mock<Service>(); // using Moq | |
sut = new Sut(srv.Object); | |
sut.someAction(); // Exception happens here | |
SomeAssertionOverHere(); | |
} | |
// Production code | |
public class Service(){ // dependency | |
public virtual async Task<string> SomeQuery(){ // virtual is mandatory in order to mock it | |
return "someResult"; | |
} | |
} | |
public class Sut(){ // system under test | |
Service srv; | |
public Sut(Service srv){ | |
this.srv = srv; | |
} | |
public async Task SomeAction(){ | |
var result = await srv.SomeQuery(); // NullReferenceException in here | |
.... | |
} | |
} |
The problem is that Moq will make the stubbed method return null when invoked, because we are not specifying any return value. The default value for Task
[Test] public async Task | |
filter_results_from_search(){ | |
var srv = new Mock<Service>(); | |
srv.Setup(s => s | |
.SomeQuery()) | |
.Returns(Task.Factory.StartNew( | |
() => "someQuery") | |
); | |
sut = new Sut(srv.Object); | |
sut.someAction(); | |
SomeAssertionOverHere(); | |
} |
The key is to return a new task: Task.Factory.StartNew(lambda)
Originally published in Carlos Blé's blog.