Unit Testing Asynchronous Code in Silverlight

My current job is a very enjoyable one. I have the pleasure of working for Rocky Lhotka on CSLA 3.6 and CSLA Light, more specifically CSLA Light but the two definitely bleed together.

CSLA Light is a project where we are trying to create a version of CSLA that will run on Silverlight. If you’re interested in hearing more details about this you should check out Rocky’s blog since it is the most up to date authority on CSLA development progress.

One of the problems we ran into right away was the ability to unit test our Silverlight code. Unit testing on Silverlight presents a number of limitations that are not present in a standard .net application. We initially tried using the unit test framework provided by Microsoft but found it impossible to test anything with a WCF service call in it due to threading.

To help illustrate the problem consider the following test:

[TestClass]

publicclassTestExample

{

[TestMethod]

publicvoid Example()

{

ManualResetEvent mre = newManualResetEvent(false);

BackgroundWorker worker = newBackgroundWorker();

worker.DoWork += (o, e) =>

{

// Do some processing…

};

worker.RunWorkerCompleted += (o, e) =>

{

mre.Set();

};

worker.RunWorkerAsync();

mre.WaitOne();

}

}

This test simulates a unit of work that is performed asynchronously. If you run this test in Silverlight what will happen? Also, suppose in your DoWork method there is bug and an Exception is thrown, what will happen? I’ll get back to this in a moment.

One of the features of CSLA is something called the “Data Portal” which is a concept that has been preserved in CSLA Light with only some slight differences, primarily all network calls in Silverlight must be done asynchronously. The Data Portal is the mechanism your CSLA business objects must use to transfer data across network separated application tiers. In CSLA 3.6 an asynchronous Data Portal has been created to provide parity with Silverlight, not to mention the fact that it’s just plain cool.

One interesting thing to know about Silverlight is that whenever you use WCF to make a network call it will dispatch that call onto the UI thread. I believe this is actually a limition of the browser rather than Silverlight itself, it must be piggy backing on top of the browser XmlHttpRequest functionality and therefore suffers from the same limitations.

This is a major problem for the Silverlight MSTest framework! Since your test is running on the UI thread if your test tries to make a WCF call it will need to be dispatched to the UI to work and you will end up with a deadlock. The above test will not work in Silverlight because we have used a ManualResetEvent to hold the UI thread since it too will dispatch to the UI thread.

So to respond to this we came up with a light weight unit testing framework that will allow you to easily test asynchronous code in Silverlight and to accompany NUnit or MSTest in .NET. The project is on Codeplex and it is called Unit Driven. It is designed to allow you to easily test asynchronous code in both Silverlight and .NET. In fact, in CSLA the test code we are writing is identical for both CSLA and CSLA Light despite the various differences in the environments. Here is an example of how you would write the previous test to address all of the questions I posed using Unit Driven:

[TestClass]

publicclassTestExample : TestBase

{

[TestMethod]

publicvoid Example()

{

UnitTestContext context = GetContext();

BackgroundWorker worker = newBackgroundWorker();

worker.DoWork += (o, e) =>

{

// Do some processing…

};

worker.RunWorkerCompleted += (o, e) =>

{

};

worker.RunWorkerAsync();

context.Complete();

}

}

The subtle differences in this approach are simply that you’re using the UnitTestContext object to block the thread, or not, for you depending on the framework your test is running in as well as having an AsyncAsserter to manage getting exceptions back to the test thread for you.

In the first example you would end up in a deadlock in Silverlight and if your DoWork method threw an exception in either framework it would be interpreted as an unhandled exception and would cause your test to hang. With UnitDriven we are able to manage this easily by using an Assert.Try( … )