This post will look at using the Silverlight Unit Test Framework to make it possible to use TDD to test a ViewModel (MVVM) that is making asynchronous calls to ADO.NET Data Services.
With Silverlight 3, we have a collection of new ideas, technologies, and practices that sometimes collide in interesting ways. For example
- Most sane people agree that data access in Silverlight should be done asynchronously to avoid blocking the UI thread and angering users.
- Model-View-ViewModel is emerging as the most common way to loosely couple Silverlight application architectures.
- TDD, or at least test-first development, is becoming increasingly recognized as a “good thing”.
If you’re anything like me, and you tried to combine these three things, you would find to your dismay that
- The major unit testing frameworks don’t support Silverlight testing yet. They expect to find binaries that are compiled against the full .NET framework.
- These same unit testing frameworks don’t have anything built-in for asynchronous unit testing. There are some things you can do about that, but it makes for hard-to-read, long tests.
Luckily, the Silverlight Unit Test Framework, now a part of the Silverlight Toolkit, goes a long way towards solving these problems.
The ViewModel
To start, let’s look at the ViewModel that we want to test.
1: public void Load()
2: {
3: DataServiceQuery<Meal> dataQuery = _context.Meals;
4: dataQuery.BeginExecute(LoadCompleted, dataQuery);
5: }
6:
7: private void LoadCompleted(IAsyncResult ar)
8: {
9: var qry = ar.AsyncState as DataServiceQuery<Meal>;
10: if (qry != null)
11: {
12: var results = qry.EndExecute(ar);
13: foreach (var meal in results)
14: {
15: Meals.Add(meal);
16: }
17: }
18: InvokeLoadComplete();
19: }
This ViewModel has one public method (Load()) that communicates with ADO.NET Data Services asynchronously. When the data returns, the LoadCompleted() method is responsible for taking the data returned and using it to fill up a Meals property. This Meals property is what our UI will bind to.
All we want to do is test that when the Load() method is called, the Meals property will be populated … eventually. We can tell when the Meals property should be filled up because the ViewModel kindly fires off a LoadComplete event.
The Test Project
The first step is to create a test project following the instructions available on Jeff Wilcox’s blog. There are two sets of instructions there – I followed the manual ones so I could be sure of what was going on.
At this point, we’ll have two Silverlight applications: our real one, and the test one. That looks like this in my Solution explorer.

The Test
Let’s take a look at what the test looks like.
1: [TestMethod]
2: [Asynchronous]
3: public void Load_PopulatesAListOfNonNullMeals()
4: {
5: bool done = false;
6:
7: var viewModel = new HomeViewModel();
8: viewModel.LoadComplete += (() => done = true);
9:
10: EnqueueCallback( viewModel.Load );
11: EnqueueConditional( () => done );
12: EnqueueCallback( () => Assert.IsNotNull(viewModel.Meals) );
13: EnqueueTestComplete();
14: }
Lines 1 & 2: Our method is decorated with the standard MSTest “TestMethod” attribute to identify it as a test and is also decorated with “Asynchronous” to identify it as, well, an asynchronous test.
Line 5: Here we set up a boolean that we can check later to see if the asynchronous call has been completed. Remember that our ViewModel fires off a “LoadComplete” event when it is finished talking to ADO.NET Data Services.
Line 7 and 8: We create the ViewModel and use a lambda expression to set our “done” boolean to “true” when the “LoadComplete” event fires. This means that we can rely on our boolean to tell us if the “LoadComplete” has fired yet or not.
Line 10: This is the first of our Silverlight Unit Testing Framework nuttiness. The “EnqueueCallback” method is available to us if our unit test inherits from SilverlightTest, which it does.
1: [TestClass]
2: public class HomeViewModelTests : SilverlightTest
3: {
4: }
“EnqueueCallback” tells our test to queue up some work to do. In this case, the specific work that we’re asking the test to queue is the call to the Load() method on our ViewModel. We have to use the “EnqueueCallback” syntax because the Load() method is going to be doing work asynchronously. If we just called Load() and our test moved on, our test would arrive at its conclusion before any data came back from the server.
Line 11: This is the backbone of the test. The “EnqueueConditional” method essentially tells our test to “hold your horses until the condition that I specificy is true”. In this case, we’re having it look at our “done” boolean. This prevents our test from moving forward until the ViewModel “LoadComplete” event is fired, since the boolean is tied directly to that event back on line 8. You can put a breakpoint on this line and watch it get called over and over while the tests wait for “done” to be true.
Line 12: This is our standard assert method, but wrapped up in the “EnqueueCallback” method since we’re still working asynchronously. Basically, once you start in with the “Enqueue” methods, you stick with them until the end of the test.
Line 13: This tells our test that we’re all done, and it needs to report back what happened. If you omit this line, the test will run forever. Or until you kill it.
Running the Tests
To run the tests, all you have to do is set your test application as the startup project, and hit F5. Unless you are using ADO.NET Data Services as your back-end, that is. Silverlight is not allowed to communicate with ADO.NET Data Services across servers, so your tests have to be running on the same server as your back-end data source. If you see an ‘Access Denied’ error message running your tests, that’s why.
Fortunately, this is an easy problem to overcome. In the Properties tab for the web project that is hosting your ADO.NET Data Services, just add the Silverlight testing project to the deployed Silverlight applications.
Now you can set the startup project to be your web project, and set the startup page to be the host page for your Silverlight testing application.

Conclusion
And that’s that. It’s a lot of hoops to jump through, and the tests look a bit odd and hard-to-read, but they would be odder and harder-to-read if you did all the async plumbing yourself, I would imagine.
Hopefully, the other unit testing frameworks will release their own solutions to this problem soon so we can have some good alternatives. But in the meantime, this accomplishes my goal of doing TDD with MVVM in Silverlight.