C# 5.0 Async-Feature: Unit Testing Part II, Synchronization-Context Again?
Disclaimer: This post is based on the C# 5.0 CTP. Everything described here is subject to future changes.
Last time we explored some implications of the C# asynchronous operations for unit tests. We noticed that we need to orchestrate tests to honor the asynchronous nature of our code. This time we take a look at another pit fall. Again this example assumes that we’re developing a small desktop application.
Let’s start: This time our business operation sums up a value, let’s say it sums up money. Since it’s hard to earn a Dollar/Euro/Swiss Franc it takes a while. And to earn more we do this money earning process twice. Our synchronous code works just fine.
The Implementation
The Test
As said the method ‘EarnOneDollar’ takes a while. To improve the reactiveness of our desktop application we make this process asynchronous. First we make the ‘EarnOneDollar’-method asynchronous. In the ‘EarnMoney-‘method we start the two sub task asynchronously and then wait until everything is done. In the test we wait for the result and then check if the property has the right value.
The Implementation
The Test
After this refactoring we run the tests. To our surprise the test fails sometimes. And it fails with different values!
A Race Condition, Oh noes!
Obviously we’ve introduced a race condition. However when that code runs in the regular desktop application it works fine. So why does it fail in the test setup? Remember my post about the synchronization context? This bites us here: Since there’s no synchronization context in the NUnit test-runner the continuations are executed on the thread-pool. This also means that the synchronization of our money counter variable is wrong. How do we fix that? Well we could synchronize the variable, but that’s quite ugly and bloats the code. I suggest to setup the environment so that it mimics a desktop application. This means that there’s a message pump and a synchronization context.
Let’s introduce a special ‘test’-message pump. It is based on the example implementation from my previous post. This message pump processes messages until some special condition is matched. We create a static utility method which sets up the message pump and sets it as the synchronization context. This method expects a closure which contains the test to run. Additionally it passes an ‘awaiter’ to that test-closure. The ‘awaiter’ allows us to wait on a task and process messages meanwhile.
The Test Utility
The Message Pump / Synchronisation Context : Just the link, to keep this post smaller. It’s essentially the same as in my previous post.
Now we’re ready to update the test. We use this nice utility to provide a synchronization context for our test. We wrap our test in a closure and use the awaiter to wait for the process to finish
Conclusion And Follow-Up
This time we’ve looked at an example where the unit test fails due to invalid synchronization. It’s really important to understand the concept of the synchronization context also in unit tests. To ensure correct synchronization we’ve also introduced a utility class which sets up a special synchronization context for a test.
There’s more to come about async operations and unit testing. Meanwhile: Have a fun time playing with the Async CTP yourself.
- C# 5.0 Async-Feature: Unit Testing, Part I
- The Wire Season 4-5
The problem lies in two threads attempting to increment the same integer at the same time.
moneyEarnedSoFar needs to be protected against this issue. That is the reason why your tests failed.
google ‘thread safe increment C#’ to get thread safe methods
You didn’t read the post or didn’t understood it. The async operations don’t imply multithreading: The whole idea is that you don’t need to synchronize and don’t have to go into all that trouble. But this only works with a correct synchronisation-context, as explained in the post.