Now you might be wondering where part 1 is at and I fully agree that this looks weird. The reason for me to start with part 2 is because part 1 of unit-testing with silverlight is actually covered pretty well by Scott Guthrie and I don’t want to do his work all over again.
In this article I’m going to show a bit more on how you can create unit-tests for async operations in silverlight. There are quite a few scenario’s where you’re going to need async operations to get things working in silverlight, so doing the right thing when unit-testing these is important.
Unit-testing basics
Okay, leaving out part one of the series doesn’t really help me explain this part, so here’s a short introduction to unit-testing in silverlight.
Unit-testing in silverlight works almost the same as with the microsoft unit-testing framework. In fact, they’ve done a great deal of work to make it look the same. What you need to get started is the silverlight unit-test framework this contains a set of assemblies needed to create and run unit-tests for silverlight.
Checkout Scott Guthrie’s introduction if you want to know how to get started with unit-testing in silverlight.
Testing asynchronous methods
Most communication related things in silverlight are building the following pattern: method with a name like OperationAsync and an event with a name like OperationCompleted. Or the pattern BeginOperation, EndOperation with callbacks. These patterns require a whole different kind of strategy when testing them. To test these patterns you need to be able to start a test and finish it after the async operation is actually completed. In silverlight this means that the unit-test does get started, but the test case should notify the test harness that it is completed.
The test subject
I’ve created a class that implements an async operation to demonstrate the principles of testing async operations in silverlight. It offers a method to begin download webcontent from a webserver and has an event to indicate that the operation is completed.
The test subject looks like this:
1: public class AsyncTestSubject
2: {
3: public event EventHandler<OperationCompletedEventArgs<string>> GetWebContentCompleted;
4: 
5: public void BeginGetWebContent(string uri)
6: {
7: HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(
8: new Uri(uri,UriKind.RelativeOrAbsolute));
9: 
10: request.BeginGetResponse(new AsyncCallback(OnGetResponseCompleted),request);
11: }
12: 
13: private void OnGetResponseCompleted(IAsyncResult result)
14: {
15: try
16: {
17: HttpWebRequest request = (HttpWebRequest)result.AsyncState;
18: HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);
19: 
20: using (Stream responseStream = response.GetResponseStream())
21: {
22: StreamReader reader = new StreamReader(responseStream);
23: string webContent = reader.ReadToEnd();
24: 
25: PostOperationCompleted(null, webContent);
26: }
27: }
28: catch (Exception ex)
29: {
30: PostOperationCompleted(ex, null);
31: }
32: }
33: 
34: private void PostOperationCompleted(Exception error,string result)
35: {
36: if (GetWebContentCompleted != null)
37: {
38: Deployment.Current.Dispatcher.BeginInvoke(() => GetWebContentCompleted(
39: this, new OperationCompletedEventArgs<string>(error, result)));
40: }
41: }
42: }
The test fixture
To test the class that I just created, I’ve added a test fixture that will try to download web content from an existing location. This should complete without errors and should give me an HTML page. I don’t really care what page it will download, as long as it downloads it.
1: [TestClass]
2: public class AsyncTestCase: SilverlightTest
3: {
4: [TestMethod]
5: [Asynchronous]
6: public void TestGetWebContentForExistingPage()
7: {
8: AsyncTestSubject subject = new AsyncTestSubject();
9: string uri = "http://www.google.com";
10: 
11: subject.GetWebContentCompleted += (sender, e) =>
12: {
13: Assert.IsNotNull(e.Result);
14: Assert.IsNull(e.Error);
15: 
16: EnqueueTestComplete();
17: };
18: 
19: subject.BeginGetWebContent(uri);
20: }
21: }
When working with async tests you need to make the fixture of the SilverlightTest type, this enables various callback scenario’s to the unit-test host. The next step is creating a testmethod that is marked with the Asynchronous attribute. It’s important to keep in mind here that the runtime does not wait for the test to be finished. Instead it will keep track of the test and will mark it completed when the method EnqueueTestComplete is called.
The test scenario is basic here, normally you would want to check to contents of the response for possible errors. However this is sufficient to demonstrate the purpose of asynchronous unit-tests.
Tips and tricks
There are a few handy tricks to make working with async operations in silverlight and testing these methods:
- Deployment.Current.Dispatcher
In general when you are working with background threads to perform async operations it’s a good idea to use Deployment.Current.Dispatcher to perform callbacks that need to be handled by the user interface. The application will hang or throw exceptions if you try to call UI logic without letting the dispatcher handle the call.
- The AutoResetEvent class
Avoid using AutoResetEvent, it’s most likely that this will hang the application. In fact, I’ve seen a few cases where it hangs internet explorer/firefox too. The best way to implement async operations in silverlight is to use eventing as much as possible. Some things can be mind bending, but it really helps solving nasty lock situations.
- Need to wait for something in the unit-test?
It can happen that you need to wait for something to complete. You shouldn’t need to wait with Thread.Sleep, but it can be useful sometimes to wait for a specified number of milliseconds. You can use the EnqueueSleep() method on the SilverlightTest class to accomplish this.
Conclusion
The support for unit-testing async operations in the silverlight unit-testing framework is excellent and a must have in my opinion. The programming model with the async operations in silverlight is the way to go, but it can be quite tricky at first. Having a good unit-test will save you in those cases.
I hope this helps get people going with silverlight by allowing them to deliver even more solid good looking software!