We're developing a Line of Business (LOB) Silverlight application which we are attempting to establish some good development patterns that allow us to create client side assemblies that follow good design practices, are easily testable and can be used by more than one type of client e.g. WPF and Win Forms. I'll try and blog about the experiences we have doing this.
One of the first requirements we have for creating our application is that it can be easily tested both via code and a user interface test framework. Code testing turns out to be fairly easy to do, and it appears that actual UI testing can be done but I'll talk about that when we've got it working.
We're taking a test driven development approach so getting testing via code is the first requirement we need to solve. We've found too approaches for testing Silverlight applications:
- Using a Silverlight based nunit like testing framework
Tests are actually run within Silverlight itself and use custom attributes. Test assemblies cannot be run using the standard Visual Studio mechanism, instead run within the browser. http://code.msdn.microsoft.com/silverlightut
- Using Standard Visual Studio Test Projects
Tests are written in normal test projects altered to work with the Silverlight assemblies that they are testing.
We've chosen to follow the second approach as it works well for developers using visual studio and more importantly with the build server when it performs continuous integration and overnight builds.
Sharing Silverlight Assemblies
Assemblies generated Silverlight applications can be referenced by standard assemblies outside the Silverlight runtime inside a normal Dot Net runtime. All that needs to be done is to remove all the System assembly references in the normal assemblies and replace them with the System assemblies from the Silverlight installation. This is very useful when coming to unit test Silverlight code as normal unit tests projects can be used. Care must be taken not to introduce relative paths to the referenced Silverlight assemblies, I've been unloading the project and editing the references after adding them directly in the .csproj files to take care of this.
Before editing the .csproj file after adding the Silverlight assembly references using Add Reference->Browse
<Reference Include="system, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\Program Files\Microsoft Silverlight\2.0.31005.0\system.dll</HintPath>
</Reference>
<Reference Include="System.Core, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\Program Files\Microsoft Silverlight\2.0.31005.0\System.Core.dll</HintPath>
</Reference>
The .csproj file should contain references like:
<Reference Include="system, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, processorArchitecture=MSIL">
<HintPath>C:\Program Files\Microsoft SDKs\Silverlight\v2.0\Reference Assemblies\system.dll</HintPath>
</Reference>
<Reference Include="System.Core, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, processorArchitecture=MSIL">
<HintPath>C:\Program Files\Microsoft SDKs\Silverlight\v2.0\Reference Assemblies\System.Core.dll</HintPath>
</Reference>
In theory all our client assemblies apart from ones containing the Silverlight views can be shared between WPF and Silverlight. The application has an assembly structure that separates out the behaviour of the client from its views using MVVM and MVP patterns. The idea is that the WPF client (and unit test assemblies) reference all the assemblies that do not contain Silverlight user controls sharing all the presenter and view model logic.
One potential concern of this approach is that it appears that the Silverlight assemblies are used directly by non Silverlight assemblies. I.e. a WPF application references the shared client assemblies, it then must reference the Silverlight assemblies. For types such as string being part of mscore this isn't a problem as the type gets translated to the correct mscore type used by the running framework (i.e. not the Silverlight one). For other types this translation doesn't occur, so a type such as XmlDocument will be running the IL contained in the Silverlight assemblies not the main framework ones. In effect this forces all client side assemblies regardless of the client type (i.e. WPF / Winform) to only reference Silverlight assemblies.
An alternative to this approach would be to perform a dual compile of the shared assemblies where Silverlight and standard assemblies are created. This is the approach that the Prism project appears to use (http://www.codeplex.com/CompositeWPF) for creating the Composite WPF/Silverlight toolkit, they might even have some tools to help with this. Its also pretty easy to do in Visual Studio by just creating two projects, keeping the code in one and adding the code via links in the other. We're going to take these approaches as the need arises though.
Testing
We've now got a testable Silverlight application, the non view UI code is in its own assemblies separate from the Silverlight application assembly. The non view UI code is now testable using the standard Visual Studio tools.
Future Investigation
I'm currently looking at how WCF services can be shared between Silverlight and other non silverlight assemblies, so should be able to blog about the solution we've found soon.
Other people in the team have been looking at using following areas:
- Use of Prism http://www.codeplex.com/CompositeWPF
- Defining styles for Silverlight controls (we've found issues defining application wide styles for controls outside the System.Windows assembly)
- Creating a framework for modal Dialogs