I’ve already discussed some implications of the async feature in my previous post. In this post I focus on some challenges for unit test. Once again I assume that everyone is informed about the basics of the new feature. Otherwise I recommend to visit the official CTP-site which links to videos, blog-posts, white-papers and the binaries to try it yourself.
Let’s examine how the async-stuff affects unit testing. Suppose we write a small desktop application which does some work in the background. We’re going write some tests for it. First lets start with a simple, classic test without any async operations.
Since calculating the string ‘Tons of money’ takes a long time and leaves our GUI unresponsive we’re going to improve the situation. We’re taking advantage of the new async support. Since this tiny example doesn’t have any real asynchronous operations we just spin off a task which sleeps of a while. In real application you would call other async API’s which do the work. Then we refactor the method ‘AwesomeBusinessOperation’ to return a Task of string and use the await operator in the sub-operation. We also add the suffix ‘Async’ to the methods to distinguish them from regular operations.
Now we change the test. Luckily this is very easy. Instead of using the result directly we just use the Task.Result property. This will block the thread until the calculation is done. This also means we’ve no trouble testing it, we just can wait for the result. So we can refactor to async operations without making our tests a nightmare.
Now our example about is quite naïve, isn’t it? I mean we just calculated some result, which is easy to test. Let’s look at a example where we have some side effects. A typical example for a desktop application is to trigger events. Again we start with a simple, synchronous example.
Like before we want to change ‘AwesomeBusinessOperation’ to run asynchronously. Again this is not that hard. Add the async modifier and ‘await’ other asynchronous operations. The rest is the same as before.
Since our test is only interested in the event, we don’t need to change the test. How cool is that? However the test fails. Oh oh, bad news =(.
Why does our test fail? Well the operation does actually what it’s supposed to do: It runs asynchronously. The code initiates our asynchronous operations and the returns to the caller. In our test it returns to our unit test. This test now checks if the event was called. But the operation which triggers the event is still running and hence the test fails. For our example we can fix this by waiting in the tests. This is possible since our method returns a task anyway. We can use the Wait-method on the task. After this change the test works again.
Conclusion And Follow-Up
Well we’ve seen that some unit tests can easily be adapted for async operations. We’ve also seen that some async operations may break our tests and we need to introduce additional awaits.
As you may noticed this isn’t the full story. We are still using rather naïve examples. What if the async operation returns void and we cannot use the returned task to wait for completion? Does the synchronization really work correctly? There’s more to clearify. I’m going to tackle this issues in follow up post. Stay tuned.
- C# 5.0 Async-Feature: Be Aware Of The Synchronization-Context
- C# 5.0 Async-Feature: Unit Testing Part II, Synchronization-Context Again?