Testability is becoming a common design goal. Patterns like dependency injection help us build code that is easier to test, and design for testability is something Microsoft is embracing in ASP.NET MVC framework. I was pleasant surprised to discover some of the testing capabilities in Silverlight 2 Beta 1. It’s not as baked into the product as with ASP.NET MVC, but as part of the Silverlight 2 controls download you get a full Silverlight unit testing framework you can start using today. It’s the same framework Microsoft uses to test their Silverlight controls, and the source download of the Silverlight Controls include the entire test suite.
Jeff Wilcox works on the Silverlight Controls, and has written an excellent tutorial on unit testing with Silverlight 2. It covers the basics of setting up the test framework, as well as how to write API and UI tests for Silverlight. If you want to read up on the basics I strongly recommend his blog post. If you want to print the tutorial Jeff also made the tutorial available as a downloadable PDF.
Jeff does not go into some of the more "advanced" testing scenarios, like how to test asynchronous code, so this post picks up where he left. Most unit testing frameworks makes you write your test methods as public parameter less methods that return nothing. You tag your method with some kind of testing attribute to tell the test runner that this method is a test. When your test method returns the test framework moves on to next test and execute it. This behavior is causing some challenges when writing tests for asynchronous code.
Back in September 2007 I wrote a post on how to unit test event-based asynchronous code using the ManualResetEvent object for synchronization. The even-based asynchronous pattern was introduced in .NET 2.0 and makes it easier to do common asynchronous tasks like calling web services or downloading content from the web. If you add a reference to a web service that has a "GetOrders" method, you will an additional "GetOrdersAsync" method on our proxy class, as well as a "GetOrdersCompleted" event which is the callback event.
Silverlight 2 uses the same model, but takes it one step further as you now only get asynchronous versions of the methods on your web service proxies. The same is true for the WebClient class, or the HttpWebRequest class you might use to talk to REST services. The reason for this behavior is that the Silverlight 2 network stack is built on top of the browser networking stack, and behaves the same way you’re used to from writing AJAX pages (after all ,the A in AJAX stands for asynchronous). The reason to do asynchronous network calls is to not block the UI thread, and give the user a better experience.
Over the last couple of months I’ve been doing Silverlight 2 development fulltime, and so far the focus has been on building "working prototypes" and exploring UI ideas rather than building production reading code. Now with Beta 2 just days away we’re ready to take the project into the next face, and need to make sure we do proper testing. This week I’ve been playing with the Silverlight 2 testing framework, and I figured I could share some of the experiences around writing asynchronous tests.
The application I’m going to use for my examples is the YouCard demo from REMIX. It was written without testing in mind, so I figured it would be an interesting exercise to go back and try to test the code. The main class in the YouCard application is the YouCardData class. By using data binding I got some separation concern from the start, and the responsibility of the YouCardData class is to talk to Twitter and Flickr and download information about a specific user. Whenever the data changes the YouCardData object will notify the UI through the INotifyPropertyChanged interface as well as updating ObservableCollection<T> collections.
The first test I want to use as an example is a test to verify that changing the Twitter username on the YouCardData object is going to download the Twitter feed and update the user information. The web request to get the Twitter feed is asynchronous, and therefore we need a way to "hold" the test until we’re ready to do assertions on the data we got back. To write an asynchronous test you need to derive from this SilverlightTest class, as well as mark your method with an Asynchronous attribute. By deriving from the SilverlightTest class you get access to several "EnqueueXXX" methods. These methods let you "queue" up code blocks (delegates) with the code you want to execute asynchronous. In the example below we queue one method to change the twitter username, then we use the "EnqueueConditional" which will wait for a condition to be true before executing the next block in the queue. In this case we’re using a flag to tell if the data is returned from the server. If so we execute two code blocks containing our assertions, before calling EnqueueTestComplete to let the test framework know our test is complete.
The second test I want to show is a UI test simulating a user entering two Twitter users, clicking the add button, and then clicking the close button on the YouCard control to remove the user. To do this I had to change the protection level for my click handler from private to internal, and add the InternalsVisibleTo attribute to the application. Jeff has the details covered in his introduction tutorial. The reason this test is asynchronous is that we run a "shrink" animation before actually closing the card. So we have to wait for the animation to complete before we can do assertions on the numbers of cards on the page.
Another thing worth mentioning when doing UI testing (Jeff also covered this in his tutorial) is that all application specific resources (such as styles and templates) needs to be either copied over to the Test application project, or made local to the user control using the resource. So if you’re getting resource not found exceptions check what you got in your App.xaml in the application you’re testing, and make sure you got the same stuff in the App.xaml of your test project.
I’ve uploaded a new version of my YouCard sample application, including the test libraries (so you don’t have to download the Silverlight 2 Control Source to test the app), as well as the test project containing 9 tests. The tests include both API tests for Flickr and Twitter as well as UI tests for adding/removing cards and accessing local storage.
One of the cool things about the Silverlight test framework is that the test runner is actually the browser and Silverlight itself. I actually deployed the tests to my server so you can see what the test framework looks like when executing the tests.