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