Unit Testing Asynchronous Code in C#

C# Programming Guide

Unit testing is a technique of testing units or blocks of code to verify if it conforms to the desired functionality. Unit testing asynchronous code is not straightforward because of the nature of execution of asynchronous methods. This programming tutorial talks about unit testing, its benefits, and how developers can unit test asynchronous code in C#.

Looking to learn C# in a class or online course setting? We have a list of the Best Online Courses to Learn C# to help get you started.

What Is Unit Testing?

Unit testing refers to verifying the functionality of a software module or program by individually testing each function to verify if it conforms to the desired results. This process helps to ensure that the code is correct, reliable, and meets the specified requirements. The purpose of unit testing is to detect and prevent defects at an early stage in the software development process.

Unit testing has several benefits such as the following:

  • It improves code quality by identifying and preventing defects before they cause system failures.
  • It can help reduce the cost of bug fixes since the bugs are detected early in the software development life cycle.

What is Asynchronous Programming?

Asynchronous programming allows resource-intensive operations without blocking the application’s main or execution thread, thus enhancing the throughput while also ensuring the responsiveness of the user interface.

When developers invoke an async method in their code, the caller thread may continue to do other tasks while the async method is in execution. Hence, your application will probably be more scalable and responsive if you implement asynchrony.

Asynchronous programming allows programmers to run multiple operations concurrently and then wait for them to finish before continuing with the next phase of your program. Asynchronous programming also makes it easier to manage complex sequences of events, such as handling user input, reading and writing files, or accessing databases.

Asynchronous programming can be beneficial because it allows you to perform tasks without blocking your application’s main thread. The downsides of asynchronous programming are that it can be challenging to debug and maintain the code since it introduces complexity into your codebase.

Challenges of Unit Testing Asynchronous Code in C#

Unit testing your asynchronous C# code is not as straightforward as unit testing synchronous code. Async void methods cannot be awaited because you can only use the await keyword on methods that return a Task instance. Additionally, async void methods are meant to be executed immediately, i.e., in a fire and forget manner. Exceptions thrown in async void methods cannot be caught in the catch blocks in your code.

Another problem is that asynchronous methods often do not return values in the same order they were called. If you try and call several asynchronous methods, which rely on each other, after they have been called, your unit tests can break because the results may not be in the correct order.

Additionally, it is difficult to determine when your unit test that tests asynchronous code has completed. Typically, you will want to wait until the tested method has completed its execution before checking that the expected result has been achieved.

However, if the method call takes a considerable amount of time to complete, waiting until the entire call to be completed may be impractical. That said, we will examine how we can overcome these challenges in the next section.

Read: Top Unit Testing Tools for Developers

Writing a Unit Test Method to Test an Async Method in C#

The method signatures for asynchronous methods contain the async and Task keywords. It is recommended that an await statement or statements be included within the body of an async method. If you do not have one, the async method will run synchronously. Observe that await is a unary operator, with the operand being the name of the method to be awaited.

Consider the following C# code example:

public class MySampleClass
{
   public static async Task SomeMethodAsync()
   {
       await Task.Delay(100);
       Assert.True(false);
   }
}

The following code example shows how you can write the unit test for the preceding method in C#:

public class MyUnitTest
{
    [Fact]
    public void MyAsyncMethodTest()
    {
        MySampleClass.SomeMethodAsync();
    }
}

When you run the unit test, you will observe that the test succeeds. In other words, even if the assert fails because of the following statement, the unit test method succeeds:

Assert.True(false);

Here is another example of an async method in C# that throws an exception instance:

public static async Task AnotherMethodAsync()
{
    await Task.Delay(100);
    throw new Exception("This is an error message.");
}

Now, suppose you have written the following unit test method to test the above piece of code:

[Fact]
public void MyAnotherMethodTest()
{
    MySampleClass.AnotherMethodAsync();
}

When you run the unit test method, the test succeeds even if the method under test throws an exception.

When unit testing an asynchronous method, your unit test method should have the async keyword. In other words, your unit test method should be an asynchronous unit test method. Additionally, developers should specify the await keyword in their unit test method at the point where the async method (i.e., the method to be tested) is called.

The following C# code example illustrates the updated version of the unit test method:

public class MyUnitTest
{
   [Fact]
   public async Task MyAsyncMethodTest()
    {
            await MySampleClass.SomeMethodAsync();
    }
 }

When you run the unit test this time, it fails and that is exactly the desired result. This time the unit test method works fine because the await keyword has been used at the suspension point – that is, the point at which the async method has been called.

Avoid Using Async Void in Unit Test Methods

It is recommended that programmers use async Task unit test methods in lieu of async void. The reason is that it is difficult to retrieve the test results from the async void unit test methods in C#. It should be noted that not all unit test frameworks support async unit tests that return void. On the contrary, most of the unit test frameworks around provide support for async unit tests that return a Task instance.

Note that xUnit is capable of handling async void methods in unit tests by leveraging its custom synchronization context. However, the recommended practice is that developers should avoid using async void in their unit test methods because not all unit test frameworks support it yet. So, the bottom line is, when you need to write async unit test methods, never return void.

Final Thoughts

Unit testing asynchronous code can be challenging, but it’s worth the effort. Unit testing helps programmers find bugs before they become big problems. The most important thing to remember is that unit tests should give you confidence that your software works as expected and help prevent regression when making changes later on down the road. Happy reading!

Read more C# programming tutorials and software development tips.

Joydip Kanjilal
Joydip Kanjilal
A Microsoft Most Valuable Professional in ASP.NET, Speaker, and Author of several books and articles. More than 25 years of experience in IT with more than 18 years in Microsoft .NET and its related technologies. He was selected as a Community Credit Winner at http://www.community-credit.com several times. He has authored 8 books and more than 500 articles in some of the most reputed sites worldwide including MSDN, Info World, CodeMag, Tech Beacon, Tech Target, Developer, CodeGuru, and more.

More by Author

Must Read