Using async void in xUnit tests
When I recently needed to update some existing unit tests, I noticed that many asynchronous tests were using
async void in their signature:
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
SynchronizationContextallows the developer to track the count of outstanding
async voidoperations, 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 voidtest 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 voidtests 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.
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.