Testing Async Methods with Xunit

Tags: C#, Unit Testing, Xunit

Previously, when testing asynchronous methods such as the one below, I used synchronous tests and forced the method invocation to be synchronous by using .Result.

  public class AsyncTestClass
  {

      public async Task AddAsync(int x, int y)      
{ return await Task.Run(() => x + y); } public async Task ErrorAddAsync(int x, int y) { if (x == 0)throw new ArgumentException("zero"); return await Task.Run(() => x + y); } }


This works in most cases but modern testing frameworks have evolved; and turns out I haven’t. Now you can have asynchronous test methods and it works just as good as the old way I’ve been doing it.

Here are some example tests using both techniques:

  //passes
[Fact]
public void TestSuccessResult()
{
testClass = new AsyncTestClass();
int answer = testClass.AddAsync(1, 1).Result;
Assert.Equal(2, answer);
}

//passes
[Fact]
public async void TestSuccessAwait()
{
var testClass = new AsyncTestClass();
int answer = await testClass.AddAsync(1, 1);
Assert.Equal(2, answer);
}

//fails as expected
[Fact]
public void TestFailResult()
{
var testClass = new AsyncTestClass();
int answer = testClass.AddAsync(2, 1).Result;
Assert.Equal(2, answer);
}

//fails as expected
[Fact]
public async void TestFailAwait()
{
var testClass = new AsyncTestClass();
int answer = await testClass.AddAsync(2, 1);
Assert.Equal(2, answer);
}

Testing for Thrown Exception


Forcing the tests using .Result doesn’t work so great when you want to test for a certain type of exception to be thrown. You’ll end up with an AggregateException every time. That’s the exception type returned from async methods; it wraps the actual exception.


Example

 //fails with the wrong type of exception - AggregateException
[Fact]
public void TestExceptionThrown_DoesntWork()
{
var testClass = new AsyncTestClass();
Assert.Throws<ArgumentException>(() => testClass.ErrorAddAsync(0, 1).Result);
}


If you make your test method async and await the call to the method under test, you will get the proper exception type returned.


Example

 //passes
[Fact]
public async void TestExceptionThrown_Works()
{
var testClass = new AsyncTestClass();
await Assert.ThrowsAsync<ArgumentException>(() => testClass.ErrorAddAsync(0, 1));
}


Isn’t that clean and neat? Keep on testing!

Tools Used:
Xunit 2.3.0-beta3-build3705
TestDriven.net 4.0.3360

Further reading : https://blog.stephencleary.com/2012/02/async-unit-tests-part-1-wrong-way.html

No Comments

Add a Comment