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:
[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 outstandingasync 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.