82

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.

Joe White
  • 94,807
  • 60
  • 220
  • 330
aknuds1
  • 65,625
  • 67
  • 195
  • 317

5 Answers5

83

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);
    ...
}
JotaBe
  • 38,030
  • 8
  • 98
  • 117
21

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);
    }
}
aknuds1
  • 65,625
  • 67
  • 195
  • 317
  • 10
    Good news it's supported from [NUnit 2.6.2](http://nunit.org/?p=releaseNotes&r=2.6.2), and in [Resharper 7.1](http://youtrack.jetbrains.com/issue/RSRP-332732) – andreister Jul 26 '13 at 07:31
6

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.

Charlie
  • 12,928
  • 1
  • 27
  • 31
4

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();
Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
Andrew Shepherd
  • 44,254
  • 30
  • 139
  • 205
  • This works well, thank you! We currently have no option but to use NUnit, so this has helped us a lot. We are running winRT/.net 4.5 tests written with vs 2012 in windows 8 and running the tests with a TFS 2010 agent running in windows 8. Only a custom TFS workflow NUnit activity made it possible for us to run the test. – ehuna Oct 09 '12 at 22:40
  • I dont think you should use Wait like that in a test case. For a start Wait is blocking, which I think you know. But that could cause your test to be quite long running I guess. Also since Task will wrap exceptions in AggregateException you could be testing for something that may never happen. Stephen Cleary (as other have stated) has 2 good blogs on this : http://blog.stephencleary.com/2012/02/async-unit-tests-part-1-wrong-way.html – sacha barber Mar 14 '13 at 17:24
  • 3
    @sacha - I don't understand your "blocking" argument - if you want your tests to run sequentially, of course you have to wait for one test to complete before you run the cleanup code then start the next one. Any testing of multithreaded code will involve blocking SOMEWHERE - this example just makes it obvious where it's happening. The second point is about getting an "AggregateException" instead of the actual thrown exception. Practically all of my tests are testing that no exception gets thrown. When a test does throw an exception, I always use the debugger to find out what's going on. – Andrew Shepherd Mar 14 '13 at 22:13
  • Note this is all moot anyway because nUnit now supports aynchronous tests. – Andrew Shepherd Mar 15 '13 at 00:58
  • @Andrew : Yeah you are correct I suppose it is a mute point, since xUnit, nUnit etc etc all suppotr async/await now. What I wish had happened is that the designers of TPL (therefor async/await) had of gone the more Rx (reactive extensions) route, where most of the operations (so I guess I am talking Task/Task here) took in a IScheduler. That is really handy in rx since you can create your own implementation of IScheduler or use the awesome TestScheduler. I think the scheduler is a bit hidden in TPL. You can create your own scheduler, but compared to the Rx test scheduler, TPL is lacking – sacha barber Mar 15 '13 at 08:30
2

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.

Pang
  • 9,564
  • 146
  • 81
  • 122
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194