There is one major flaw you must have encountered whilst working with Silverlight for a while: its incompatibility with unit tests! Unless you’ve been using Silverlight solely for drawing circles and rectangles, surely you must have tried to use the Silverlight Toolkit unit test project template to try to put some TDD in your project ! …most probably you were disappointed by it because it’s fairly poor…
- This project is specific to Silverlight (the Microsoft test assembly is specific)
- Running tests is only possible by launching the test application wrapping up your test classes. And let’s be frank, the usability of this interface is quite poor. There is no integration in Visual Studio.
- It’s impossible to properly analyze the tests results and metrics or even to navigate through tests and code like you’d want to.
- It’s also impossible to run a specific test or test class without having to pollute your tests with additional tags. And then you will still have to run the application to see it play the selected tests.
- It’s almost impossible (or fairly complicated) to integrate those tests in your continuous integration build process. Statlight (an open source project) is trying to provide a way out but this remains highly dependent on your configuration, and frankly, i never had it working on a TFS build. (this can be explained by the fact that it still needs to open a window to run your tests while your build process is highly relying on commands only)
All those issues can find an explanation in the fact that the Silverlight framework is a subset of the .NET one making them incompatible with each other.
However, even though these frameworks are different, they’re also very similar. Thanks to this, we will be able to go around the limitation stated above and set up a solution to properly unit test our Silverlight libraries. This solution relies on two parts: structure of the Silverlight projects and then unit testing them within the visual studio framework.
First, architecture your projects
One of the major secrets of a long running application resides in its architecture. This goes the same for the GUI (Graphical User Interface). We’re often tempted to consider the GUI as a whole monolithic bloc:
“GUI is easy, not complex enough to structure it”
“anyway, if I decide to change my user interface, I’ll probably change the whole layer, so it’s ok to mix everything here”
Good practices don’t stop at the service layer ! A good structured GUI will allow you to optimize its testability while abstracting models from views. I’m not here to remind you of the MVVM pattern, the idea here is to go one step further by physically abstracting the different parts to leverage flexibility.
I’ll be taking the example of an application exposing web services to a Silverlight light client. (It could as well be a Silverlight only application, its structure would remain the same).
The above architecture is composed of 3 RIA projects and 1 Web project:
- This library holds (like you could guess) the application views, either controls or pages. This will help us isolate the GUI behaviors (you know, the dirty fixes you do to please the user). All references to converters or ViewModels will be handled through Binding to “StaticResources”.
- Here, we’ll isolate the GUI intelligence (or even business rules) using ViewModels, or also converters for example. We’ll also add the service references to this project (proxy generation) because this is what our models will use to manage data. If you could test only one project, this is THE library you’d want to test !
- This is our main application, and actually the only “Silverlight Application” project. It references the other libraries and will use them through application resources (App.xaml). To be able to run the application without issues, the trick here is to add the ServiceReferences.ClientConfig of the ViewModels project as a link in order to have the service information at runtime. Without this, your app will start and crash at the first service client instantiation, the service references not being present in the application.
- The Web project is exposing our web services (of course tested as well :)). It also exposes one xap file : the main Silverlight project (which will contain the two other libraries).
Then, unit test Silverlight in Visual Studio
Now that we structured our application, we can clearly identify the critical library that needs testing: ViewModels. Because this is a sensible library, we want to take advantage of the Microsoft unit test framework of VS 2010, for all the reasons I mentioned earlier. Here follows, imho, one of the most clean way to do so:
1. Create a standard .NET unit test project
2. Remove all the dll references except ~QualityTools.UnitTestFramework reference.
3. Add the following references using the browser to get them from the Silverlight SDK folder you’re using (ex.: C:\Program Files (x86)\Microsoft Silverlight\5.0.60401.0)
and activate the library local copy, this will force the test framework into using them instead of the default .NET ones.
4. Add your favorite tools libraries (RhinoMocks.. Moq…) in their Silverlight version.
5. Add the Silverlight libraries you want to test, here Octo.Bank.Web.Ria.ViewModels
Congrats, You’ve just made this unit test library running with the VS 2010 Unit Test framework under a Silverlight runtime!
Continuous Integration & Code Coverage
You are nearly there, almost ready to start developing (yes, because you’re using TDD remember ? :) ! A couple of things left to do to make sure you maximize the use of the framework:
1. Add your test library to your CI build
Using TFS up to 2008 <TestContainer Include= »$(OutDir)\%2aTests.dll » />
2. Activate code coverage metrics for your tested DLL
Using VS2010, open local.testsettings, go to Data & Diagnostics, check the Code Coverage option and via Configuration, select your Silverlight ~Views assembly.
We know how much a testing framework impacts the development life-cycle. A bad productivity tool (like the SL Toolkit way of testing) and your whole TDD cycle (red, green, red, green…) is jammed ! This is why, even if this solution has an additional cost (manually referencing libraries), I find it an acceptable one when I compare it to the shift in efficiency I get in my every day job. And this is without mentioning the comfort of having the CI build run the tests and the code coverage metrics …