NUnit & the Silverlight unit test framework
January 7, 2009
Did you know that you can run NUnit tests with the Silverlight unit test framework? In this short guide, I talk about updating NUnit to build for Silverlight, wiring up test projects, and a download with everything you need to get started.
This isn't a "metadata redirection" trick - but the actual NUnit assertions and metadata running inside the Silverlight unit test engine. This took about 15 minutes to implement from start to finish - shorter than the amount of time it'll take me to publish this post.
Though NUnit is great for legacy components and tests, I strongly recommend using the built-in Visual Studio metadata for most projects. The metadata is used by enough folks throughout Microsoft and the industry to be well-supported, tested, familiar, and not require the custom unit test provider hookup in your test projects.
That said, I do hear about legacy business objects and NUnit tests that devs would like to be able to verify and run in the browser + Silverlight environment.
You can read about NUnit attributes and assertions in the official documentation. So, here goes...
Running sample & validation tests that ship with NUnit
Inside the traditional NUnit source download, there's a sample called "Money" that tests a simple interface and types. Here's that "Money" project running on OS X/Safari, via the Silverlight unit test framework and NUnit:
"Money" sample test project: Click on the image or here to run the Silverlight test application on your machine. Tests will start in ~5 seconds.
If you have a set of legacy business objects and associated NUnit tests, you'll find it easy to run them within Silverlight now.
To further demonstrate the compatibility here, this is the "NUnitTests" test project that validates the core NUnit framework and its assertions, running inside Chrome:
"NUnitTests" that validate the NUnit framework. Click on the image or here to run the tests.
Silverlight unit test framework is easy to extend
When designing the Silverlight unit test framework, it became clear that there were three or four distinct test engine components that could be combined into a single test system. Yet, they were distinct enough to allow alternative implementations and extensions to be built.
The typical test application .XAP file ends up containing the general Silverlight unit testing engine (that performs test execution, and is very different from typical engines in that it runs on the UI thread and is synchronous), your actual tests marked with the appropriate metadata, and then the metadata and unit test assertions that you're using.
Inside the unit test engine, there's a base test harness class, and then the UnitTestHarness implementation that does most of the heavy lifting. A set of interfaces abstract out the reading and processing of unit test metadata. Interfaces aren't always a great answer, but the value added in this case.
The metadata/assertion implementation that you use (NUnit or VSTT, for example) takes care of throwing exceptions when assertions fail, that the framework then processes in a generic way.
The Silverlight unit framework ships with the same metadata used in the Visual Studio team system's unit test framework, making it easy to move between desktop and Silverlight application and test development. A "VsttProvider" sits between the Visual Studio metadata and the actual unit test engine, and ships in the Microsoft.Silverlight.Testing assembly.
OK. That's a mouthful, but it means that you can implement your own metadata provider to support running other unit tests. Since NUnit and VSTT are so similar, this was easy - but more intricate frameworks might need to modify or extend the actual unit test system to provide the right experience.
Getting NUnitFramework to build
NUnit is relatively old; it is from a different era of .NET development. NUnit utilizes the old .NET Hashtable and ArrayList types.
Also, the .NET framework base class libaries included in Silverlight are a subset, rely more on .NET 3.5 features, and exclude some of the older types.
To easily support building NUnit with the Silverlight subset of the framework, here's the list of changes I needed to make to the NUnitFramework project:
- Add simple implementations of Hashtable.cs and ArrayList.cs, utilizing modern generic collections under the hood
- Inside ConstraintBuilder.cs, update the Stack type to be a generic Stack<object>
- Added a shim/empty SerializableAttribute
- Comment out constructors in IgnoreException.cs and AssertionException.cs that were designed for serialization (lines 33-35 and 35-37, respectively)
- I removed the strong name assembly attributes from AssemblyInfo.cs, this isn't actually required.
- On line 92 of CollectionConstaints.cs, changed the DictionaryEntry typed temporary variable to be of type "var" (since the Hashtable implementation is actually exposing a generic KeyValuePair now)
So, I added a small "CompatibilityShims" Silverlight C# library project to the solution that included Hashtable, ArrayList, and SerializableAttribute.
Wiring up test applications
Unlike standard NUnit test projects, you will need to create an actual Silverlight application project for your tests. Once you create a new empty app, here's what you should do:
- Remove the Page.xaml and Page.xaml.cs files
- Add a reference to Microsoft.Silverlight.Testing.dll
- Add a reference to the built NUnitFramework and NUnitSilverlight libraries and/or projects from this download
- Inside App.xaml.cs, add using statements for the Microsoft.Silverlight.Testing and Microsoft.Silverlight.Testing.UnitTesting.Metadata.NUnit namespaces
- Update Application_Startup per the instructions below
- Add your tests and, if needed, a reference to your assembly/app under test
Here's what the startup code should look like for the test application:
private void Application_Startup(object sender, StartupEventArgs e) { UnitTestSystem.RegisterUnitTestProvider(new NUnitProvider()); RootVisual = UnitTestSystem.CreateTestPage(); }
The code is similar to the regular Silverlight unit test startup code that you use for VSTT tests, except it first registers the NUnitProvider.
You can build and run the Silverlight application like any other: just press F5 and the tests will run!
Using the Silverlight test framework's asynchronous and Silverlight testing features
Since the unit test engine is metadata-agnostic, it handles all the advanced asynchronous and Silverlight-specific test functions. Many of the core features specific to the Silverlight framework work with NUnit, therefore.
You'll find that you can use:
- [Bug(...)] attributes
- [Tag(...)] attributes. The Category NUnit attribute isn't supported in this download, but you could easily modify the provider to expose Category data as Tags.
- [Asynchronous], combined with deriving from SilverlightTest: support functional testing of user interface elements and controls.
Download the bits and samples
I'm offering this as a proof-of-concept, unsupported download. Inside you'll find everything that you need to explore the concept.
- NUnit.Silverlight.zip [266 KB], contains:
- Unit test framework binaries
- CompatibilityShims
- NUnitFramework
- "NUnitSilverlight" NUnit test provider for the Silverlight unit test framework
- Sample test applications: "Money", "NUnitTests"
Related resources
- Visual Studio unit testing intro
- Silverlight unit test framework source released
- Reference documentation for the Silverlight unit test framework
- New test framework features for RC0/RTW
- Unit testing with Silverlight 2
Hope this helps!