This project includes a port of to Silverlight. It's a faithful port, based on the current (post 1.6.1 release) source code. The intention was to change as little source code as possible, to keep the Silverlight version as close the desktop version as possible. This has been mostly successful, but due to platform or execution environment limitations, there are differences.

So what's working?

Short answer: Pretty much everything you care about.

This includes [Fact] based tests, as well as custom attribute and [Theory] based tests. Also, constructors and IDispose as setup/teardown, IUseFixture<>, new class instance per test run and so on (note that the Silverlight Unit Test framework controls the running of the tests, so random ordering is lost).

What's not working?

  1. Capturing output (yet?). Silverlight doesn't seem to allow redirecting the stdout and stderr streams, and doesn't have Trace listeners or allow redirecting Debug.Assert
  2. Various attributes from xunit.extensions aren't supported on Silverlight
    • AssumeIdentityAttribute
    • AutoRollbackAttibute (no System.Transactions)
    • OleDbDataAttribute (and so also DataAdapterDataAttribute, ExcelDataAttribute and SqlServerDataAttribute)
  3. The version independent runner API (only important if you're writing your own test runner)

Currently, most, but not all of the tests in test.xunit and test.xunit.extensions are passing (or even compiling!). There are currently 318 tests running, covering the base functionality of the library. Only two tests are failing, and these are due to being unable to capture output. Acceptance tests that invoke the compiler have been excluded.

Approach to porting

I've taken the approach of trying to change the source code as little as possible. On the whole, this has been possible through extension methods and internal classes with the same name as missing system classes. But there have been occasions where code or behaviour has had to change.

There is a readme.txt file in the xunitcontrib source with notes on how to build. It is currently a cumbersome task, and it would be nice to make this more accessable. An ideal solution would be to fork xunit. Unfortunately, the xunit tests require the xunitcontrib provider, which in turn requires xunit...


  1. Silverlight's implementation of reflection only allows access to members that you would have access to at compile time. So you might want to add the InternalsVisibleTo assembly attribute to your test classes. You can specify InternalsVisibleTo("xunit-silverlight3") or InternalsVisibleTo("xunit-silverlight4"). Or just make your code public.

On the whole, these won't bother you. But it's worth pointing them out.
  1. System.Attribute in Silverlight doesn't implement a virtual TypeId property, and various attributes in xunit override this value. I've created a class called Xunit.Attribute that derives from System.Attribute that adds this property. This class has to be public as it's the base class of other public classes. Since your code will be using the Xunit namespace, you might either get naming collisions with System.Attribute, or use Xunit.Attribute where you wanted to use System.Attribute. Fully qualifying the type name will fix this.
  2. Silverlight doesn't support XmlNode, which is used by the version independent runner. I have implemented a very narrow strip of functionality of XmlNode - just enough to get the xunit tests passing. But these are public classes. If you need to consume this (you won't unless you're writing your own runner) you should get the associated XElement and use that

Behavioural changes

There is what can be considered to be a breaking change that Silverlight's limitations has introduced. You don't need to worry about this unless you're writing your own test runner. If you're not writing a test runner, you can stop reading now.

The desktop version of uses some reflection to allow rethrowing of a previously thrown exception without resetting the stack trace. Because the tests are run via Reflection, if any exceptions leak (such as assertion failures), the exception gets wrapped inside a TargetInvocationException by System.Reflection. The desktop version rethrows the inner exception so that it looks like the real exception has crossed the reflection boundary.

Silverlight only allows Reflection to be used for late binding - in other words, you can only access class members at runtime that you would be able to access staticly at compile time. So private reflection fails. This means we can't rethrow the inner exception, and so all clients of the sdk must be ready for TargetInvocationException. This has caused changes to a number of tests.

Last edited Dec 14, 2010 at 11:11 PM by mattellis, version 5