I'm a bit puzzled here...I have a test method which does a call to an async method and I changed the signature to async Task
. Works.
[TestMethod]
public async Task TestIt()
{
bool result = await service.SomethingAsync();
Assert(result);
}
Now I read all over the web that support for async is required in unit tests and is now also in NUnit. But why is that so important? I can write the test like this, using the Result
property which will wait for Task
completion:
[TestMethod]
public void TestIt()
{
bool result = service.SomethingAsync().Result;
Assert(result);
}
Note: The purpose of async / await is to simplify the syntax necessary to consume promise-based APIs. The behavior of async / await is similar to combining generators and promises. Async functions always return a promise.
Moreover, support for async void unit tests varies across frameworks, and even framework versions. For these reasons, it's best to avoid async void unit tests.
An asynchronous task is defined by a computation that runs on a background thread and whose result is published on the UI thread. An asynchronous task is defined by 3 generic types, called Params , Progress and Result , and 4 steps, called onPreExecute , doInBackground , onProgressUpdate and onPostExecute .
Asynchronous loops are necessary when there is a large number of iterations involved or when the operations within the loop are complex. But for simple tasks like iterating through a small array, there is no reason to overcomplicate things by using a complex recursive function.
Result
has an unfortunate side effect of wrapping all exceptions in AggregateException
. This makes testing the error path much more painful.
But even if you decide you could live with that, you still have the problem of calling multiple async
methods within a single test; i.e., if your test setup also requires async
work. To do it in a blocking way, you'd have to either refactor your test method into a separate async
method, or wrap it in an async
delegate and either execute it directly or toss it into Task.Run
. Not impossible, but not convenient either.
Finally, there's the problem of async
components that assume a one-thread-at-a-time context. Examples include ViewModels and WebAPI/MVC controllers. At that level, those components often assume that they don't need to synchronize access to asynchronously shared data because they're never executed in a free-threaded context. Until you unit test, that is. The common approach is to give those unit tests a single-threaded context, e.g., installing a Dispatcher
on the unit test thread. In that case, a Result
will deadlock. There are ways around this but, again, it's not convenient to write that code.
The bottom line is that async
unit tests don't make it possible; they make it convenient. And anything that encourages unit testing (especially for trickier things like async
) is a good idea. :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With