Using async void in xUnit tests

April 15th 2022 Unit Testing .NET

When I recently needed to update some existing unit tests, I noticed that many asynchronous tests were using async void in their signature:

[Fact]
public async void AsynchronousTest()
{
    // test body
}

My first instinct was to fix them by using async Task instead, because that surely meant they were broken and would not detect failures correctly. But before I did that, I experimented a bit, and as far as I could tell, the tests worked as expected, correctly detecting failed assertions and unexpected exceptions. I decided to do some more research on the subject.

The problem with async void methods is that they cannot be awaited and can only be executed in a fire and forget manner. Any exceptions they throw cannot be caught with a catch block either. This should be a problem for testing frameworks, as they need to keep track of when each test method completes and detect any exception it throws.

However, this is not the case with xUnit. It can correctly handle asynchronous tests with async void signature thanks to its custom synchronization context. I could not find any documentation on this other than the summary comment in its code:

This implementation of SynchronizationContext allows the developer to track the count of outstanding async void operations, and wait for them all to complete.

This is exactly what is missing for async void in the default SynchronizationContext, and what seems to ensure that async void tests work fine in xUnit.

This got me curious about how other testing frameworks handle async void tests. It turns out that they do not support them. And fortunately, they are very explicit about this.

  • NUnit test runner marks any async void test method as failed when you try to run it. The message clearly states why the test failed:

Async test method must have non-void return type

  • In MSTest, async void tests are skipped and not run. This makes them stand out, so you realize that there is something wrong with them. But it's up to you to figure out why they did not run and how to fix the problem.

I have created a small sample project with an async void test and an async Task test for all three testing frameworks. You can find the code in my GitHub repository and check for yourself how they are handled.

The async void signature for asynchronous methods is a common pitfall for less experienced .NET developers. I am glad to see that all three major testing frameworks for .NET (xUnit, NUnit, and MSTest) handle test methods with async void signatures in a way that does not hide potential problems from developers. In xUnit, even such test methods work correctly. In NUnit and MSTest, such tests never pass. Instead, they are marked as failed or not run, respectively.

Get notified when a new blog post is published (usually every Friday):

If you're looking for online one-on-one mentorship on a related topic, you can find me on Codementor.
If you need a team of experienced software engineers to help you with a project, contact us at Razum.
Copyright
Creative Commons License