joydip_kanjilal
Contributor

My two cents on unit testing asynchronous code

opinion
Jan 26, 20174 mins
Software Development

Understand how asynchrony works in .Net and adhere to the recommended practices when writing unit tests to test asynchronous code

A proper and planned approach toward unit testing helps you to detect errors in your application and ensures that the application is error free. As such, unit testing has become a fundamental skill that a developer should have.

Essentially, unit testing is used to test blocks or units of code to check if it conforms to the desired results. Unit testing asynchronous code is a bit tricky — you should be extra careful when unit testing asynchronous code in your application.

Unit testing asynchronous code

Unit testing helps to detect bugs and reduce time to market. Now, as I said earlier, unit testing asynchronous code has a few challenges that you should be aware of. Consider the following piece of code:

public class IDGTest

{

  public static async Task SampleTestMethodAsync()

  {

    await Task.Delay(100);

   //Some other code

  }

}

Now suppose you write the following unit test method to test the piece of code given above:

[TestMethod]

public void WrongApproachTest()

{

  IDGTest.SampleTestMethodAsync();

}

Unfortunately, this unit test method will always pass irrespective of whether the test fails or not. Let’s understand this better — here’s an updated version of the async method we are writing our unit test for:

public static async Task SampleTestMethodAsync()

  {

    await Task.Delay(100);

    throw new Exception("An error occurred.");

  }

Note the usage of the await keyword. The async and await keywords are used to implement asynchrony in applications that leverage the latest versions of the .Net Framework. The “await” keyword is used to denote a suspension point — it captures the current SynchronizationContext and as soon as the task that has been awaited using the “await” keyword is complete, the state machine is resumed and execution of the code in the caller method restarts.

If you run the unit test method again, it will still pass. Why? The asynchronous method under test returns a Task object. It should be noted that when you are unit testing an asynchronous method, you should observe the Task object it returns. To do this, you should await the Task object being returned from the SampleTestMethodAsync method. Here’s the updated unit test method that takes advantage of await to call the asynchronous method:

[TestMethod]

public void WrongApproachTest()

{

  await IDGTest.SampleTestMethodAsync();

}

Another point you should be aware of is avoiding async void unit tests. It is quite difficult to retrieve the result of async void unit tests and hence they should be avoided. Unlike async void unit tests that are quite complicated, you can have async Task unit tests, i.e., unit tests that return a Task instance. Almost all the unit test frameworks (MSTest, NUnit, etc.) provide support for such unit tests.

An async method returns control quickly and usually returns a Task that completes its execution at a future point in time. When an exception occurs inside an asynchronous method that has a return type of Task or Task, the exception details are stored inside the Task instance. Consider the following unit test method:

[TestMethod]

[ExpectedException(typeof(DivideByZeroException))]

  public void DivideNumberTest()

  {

      Task<int> task = IDGSample.Divide(10, 0);

      task.Wait();

  }

The IDGSample class is given below. This is a static class with just one static method that returns a Task instance:

public static class IDGSample

    {

        public static async Task<int> Divide(int x, int y)

        {

            await Task.Delay(100);

            return x / y;

        }

    }

In this example, the Task class would wrap exception into an instance of AggregateException. You should not use Task.Wait and Task.Result the way this example illustrates. To unwrap the exception, we should await the Task.

Here’s the updated version of the unit test method — this time we have specified the async keyword in the method signature of the unit test method:

[TestMethod]

[ExpectedException(typeof(DivideByZeroException))]

public async Task DivideNumberTestAsync()

{

  int x = await IDGSample.Divide(10, 0);           

}

When you execute this unit test, it would provide you the desired results. The await keyword makes the difference.

joydip_kanjilal
Contributor

Joydip Kanjilal is a Microsoft Most Valuable Professional (MVP) in ASP.NET, as well as a speaker and the author of several books and articles. He received the prestigious MVP award for 2007, 2008, 2009, 2010, 2011, and 2012.

He has more than 20 years of experience in IT, with more than 16 years in Microsoft .Net and related technologies. He has been selected as MSDN Featured Developer of the Fortnight (MSDN) and as Community Credit Winner several times.

He is the author of eight books and more than 500 articles. Many of his articles have been featured at Microsoft’s Official Site on ASP.Net.

He was a speaker at the Spark IT 2010 event and at the Dr. Dobb’s Conference 2014 in Bangalore. He has also worked as a judge for the Jolt Awards at Dr. Dobb's Journal. He is a regular speaker at the SSWUG Virtual Conference, which is held twice each year.

More from this author