Perhaps you don’t want to go to the trouble of implementing the IAsyncResult pattern to accomplish some asynchronous unit of work. The async and await keywords added to C# in the .NET 4.5 framework are handy when you want to save time by using task-based asynchronous processing, or quickly turn a synchronous method into an asynchronous one. So, you’ve marked your method as async and placed the await keyword, within your new async method, on the proper line. Now, how do you unit test it?
All definition content has been paraphrased from MSDN and the Task-based Asynchronous Pattern document, TAP.docx, which at the time of this writing can be found here: http://www.microsoft.com/en-us/download/confirmation.aspx?id=19957
async: A method modifier that signals to the .NET language compiler that the marked method will run within its own thread, and that execution cannot continue past the point until the awaited asynchronous process is finished. Meanwhile, control is returned to the caller of the async method.
await: An operator that suspends the execution of the method to which it applies. When called, an asynchronous method synchronously executes the body of the function up until the first await expression on an awaitable instance that is not yet completed, at which point the invocation returns to the caller.
ManualResetEvent: A class which provides methods and functionality for one or more waiting threads to be notified, or signaled, that an event has occurred.
The example solution, which can be downloaded by clicking the example link below, was created first by selecting the Console Application Project Template. The structure of the full solution is shown below.
Testing Async Example Solution Structure
As you can see, there are two projects, one is the console project already mentioned, and the other is a unit test. You will need a new class in the console project prior to testing it. This class, ServiceInvokeWrapper, is shown below.
The ServiceInvokeWrapper class contains the method we will test. It's purpose is to ...
- Provide a method for handling the invocation of a simple, and publicly available WCF service that performs simple calculations and returns the result of those calculations.
- Contain the async method we will test, AddWrapperAsync, that invokes the add method asynchronously.
- Provide a method that simulates work being performed in some lengthy interval.
Here is the class:
Once this class is coded and compiling properly, go ahead and add the second project from project template Unit Test Project. This will get you a class identified as a TestClass, and a method identified as a TestMethod. This method is where you will add the following code to test the async method. The full implementation of the class is shown below.
The use of a ManualResetEvent object is important here. The call to WaitOne blocks the current thread until the Set method is called, signalling to the ManualResetEvent object that it can stop blocking the current executing thread and continue with executing the code after the line where WaitOne is called. You can run the test and notice that is passes due to the successFlag variable containing a value of true. Comment out all of the ManualResetEvent object usage, however, and notice the test fails. This is async in .NET 4.5 at work! That is, the delegate executed after the async work is completed, will execute sometime after the test result assertion, if the primary thread of execution isn't abandoned altogether due to the test method completing at that point. However, telling the test thread to wait for a while until the delegate, facilitated by ContinueWith, is executed, and then determine the outcome of the test is much more preferrable to a race between the async thread and the test thread.
Outcome of the unit test using the ManualResetEvent object.
So, the use of ManualResetEvent is an effective way to unit test your async method. This way, you won't have to go through the ceremony of installing your application or service in a test environment to know if your new async method is functioning as you would expect it to function within a simple unit test. Happy coding!