How do I test an async method with NUnit (or possibly with another framework)?

.NetUnit TestingAsynchronousNunitAsync Await

.Net Problem Overview


I have an ASP.NET Web API application, with an ApiController that features asynchronous methods, returning Task<> objects and marked with the async keyword.

public class MyApiController : ApiController
{
    public async Task<MyData> GetDataById(string id)
    {
        ...
    }
}

How can I write NUnit tests for the ApiController's asynchronous methods? If I need to use another testing framework I'm open for that too. I'm fairly new to .NET unit testing in general, so I'm interested in learning best practices.

.Net Solutions


Solution 1 - .Net

As of today (7/2/2014) async testing is supported by:

In the first two frameworks, the test method must have this signature:

[TestMethod]
public async Task MyTestMethod()
{
   ...
   var result = await objectUnderTest.TestedAsyncMethod(...);
   // Make assertions
}

NUnit v2.6.2+ (but before 3.0), apart from that signature, supports this one:

public async void MyTestMethod()

Of course, inside any of these test methods you can use await to call and wait on asynchronous methods.

If you're using a testing framework that doesn't support async test methods, then, the only way to do it, is to call the async method and wait until it finishes running using any of the usual ways: await, reading the Result property of the Task<T> returned by an async method, using any of the usual wait methods of Task and so on. After the awaiting, you can do all the asserts as usual. For example, using MSTest:

[TestMethod]
public void MyTestMethod()
{
    ...
    Task<MyResultClass> task = objectUnderTest.MyAsyncMethod(...);
    // Make anything that waits for the method to end
    MyResultClass result = task.Result;

    // Make the assertions
    Assert.IsNotNull(result);
    ...
}

Solution 2 - .Net

It seems to me there is no support built into NUnit 2.6 for testing async methods returning Tasks. The best option I can see right now is to use Visual Studio's own UnitTesting framework or xUnit.net as both support asynchronous tests.

With the Visual Studio UnitTesting framework I can write asynchronous tests like this:

using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class TestAsyncMethods
{
    [TestMethod]
    public async Task TestGetBinBuildById()
    {
         ...
         var rslt = await obj.GetAsync();
         Assert.AreEqual(rslt, expected);
    }
}

Solution 3 - .Net

Bear in mind that the NUnit 2.6 series, like those before it, is built to target the .NET 2.0 framework. So, NUnit can only do the same things you could code yourself, in an assembly targeting .NET 2.0.

Specifically, you can't mark your test method as async and expect NUnit to do anything special with it.

You can, however,

  • Target .NET 4.5 for your tests. NUnit will run them in a separate process.
  • Use await in your test to wait for the result of a call to an async method.

Neither of the above will give you asynchronous test execution, if that's what you are hoping for. No other tests will execute while waiting for the asynchronous operation to complete.

Another option is to use NUnitLite. NUnitLite 0.8 supports the [Asynchronous] attribute which will allow other tests to continue while your asynchronous test completes. The advantage of the attribute is that it allows asynchronous tests to work in .NET 2.0 through 4.0

We don't currently have a .NET 4.5 build of NUnitLite but it will be added soon and we are working on a change that will make use of [Asynchronous] optional in that environment. For now, you can easily download and recompile the source code for .NET 4.5.

For the future, look to NUnit 3.0 to support async methods fully along with general parallel execution of tests on multiple threads or in multiple processes.

Solution 4 - .Net

I'm in the process of converting some of my methods to async. Getting this to work with NUnit has been quite straightforward.

The test methods can not be asynchronous. But we still have access to the full functionality of the Task Parallel Library, we just can't use the await keyword directly in the test method.

In my example, I had a method:

public string SendUpdateRequestToPlayer(long playerId)

And it was tested in NUnit like so:

string result = mgr.SendUpdateRequestToPlayer(player.Id.Value);
Assert.AreEqual("Status update request sent", result);
mocks.VerifyAll();

Now that I have altered the method SendUpdateRequestToPlayer to be asynchronous

public async Task<string> SendUpdateRequestToPlayer(long playerId)

I simply had to modify my tests to Wait for the task to complete:

Task<string> task = mgr.SendUpdateRequestToPlayer(player.Id.Value);
task.Wait(); // The task runs to completion on a background thread
Assert.AreEqual("Status update request sent", task.Result);
mocks.VerifyAll();

Solution 5 - .Net

It really depends on what they're doing. Usually they'll be depending on something else which provides a Task<T> or something similar. In that case, you may be able to provide fake dependencies which allow you to control all of this in a fine-grained way. I've got a prototype "time machine" which allows you to preprogram tasks to complete at particular artificial times, then move time forward and perform assertions as you go. There's a blog post about it which you may find useful. As I say, it's only a prototype and it's not appropriate for all scenarios - but it may suit you.

Stephen Cleary also has a couple of blog posts around unit testing (1, 2), taking a slightly different approach, along with a NuGet package you may find useful.

The basic approach is the same as normal though: give your method different inputs (and dependency outputs) and test the results. It's definitely trickier achieving that with asynchrony, but it's doable.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionaknuds1View Question on Stackoverflow
Solution 1 - .NetJotaBeView Answer on Stackoverflow
Solution 2 - .Netaknuds1View Answer on Stackoverflow
Solution 3 - .NetCharlieView Answer on Stackoverflow
Solution 4 - .NetAndrew ShepherdView Answer on Stackoverflow
Solution 5 - .NetJon SkeetView Answer on Stackoverflow