Unit Testing Asynchronous Code in Silverlight

My current job is a very enjoyable one. I have the pleasure of working for Rocky Lhotka on CSLA 3.6 and CSLA Light, more specifically CSLA Light but the two definitely bleed together.

CSLA Light is a project where we are trying to create a version of CSLA that will run on Silverlight. If you’re interested in hearing more details about this you should check out Rocky’s blog since it is the most up to date authority on CSLA development progress.

One of the problems we ran into right away was the ability to unit test our Silverlight code. Unit testing on Silverlight presents a number of limitations that are not present in a standard .net application. We initially tried using the unit test framework provided by Microsoft but found it impossible to test anything with a WCF service call in it due to threading.

To help illustrate the problem consider the following test:

[TestClass]

public class TestExample

{

    [TestMethod]

    public void Example()

    {

        ManualResetEvent mre = new ManualResetEvent(false);

        BackgroundWorker worker = new BackgroundWorker();

        worker.DoWork += (o, e) =>

        {

            // Do some processing…

        };

        worker.RunWorkerCompleted += (o, e) =>

        {

            mre.Set();

        };

        worker.RunWorkerAsync();

 

        mre.WaitOne();

    }

}

This test simulates a unit of work that is performed asynchronously. If you run this test in Silverlight what will happen? Also, suppose in your DoWork method there is bug and an Exception is thrown, what will happen? I’ll get back to this in a moment.

One of the features of CSLA is something called the “Data Portal” which is a concept that has been preserved in CSLA Light with only some slight differences, primarily all network calls in Silverlight must be done asynchronously. The Data Portal is the mechanism your CSLA business objects must use to transfer data across network separated application tiers. In CSLA 3.6 an asynchronous Data Portal has been created to provide parity with Silverlight, not to mention the fact that it’s just plain cool.

One interesting thing to know about Silverlight is that whenever you use WCF to make a network call it will dispatch that call onto the UI thread. I believe this is actually a limition of the browser rather than Silverlight itself, it must be piggy backing on top of the browser XmlHttpRequest functionality and therefore suffers from the same limitations.

This is a major problem for the Silverlight MSTest framework! Since your test is running on the UI thread if your test tries to make a WCF call it will need to be dispatched to the UI to work and you will end up with a deadlock. The above test will not work in Silverlight because we have used a ManualResetEvent to hold the UI thread since it too will dispatch to the UI thread.

So to respond to this we came up with a light weight unit testing framework that will allow you to easily test asynchronous code in Silverlight and to accompany NUnit or MSTest in .NET. The project is on Codeplex and it is called Unit Driven. It is designed to allow you to easily test asynchronous code in both Silverlight and .NET. In fact, in CSLA the test code we are writing is identical for both CSLA and CSLA Light despite the various differences in the environments. Here is an example of how you would write the previous test to address all of the questions I posed using Unit Driven:

[TestClass]

public class TestExample : TestBase

{

    [TestMethod]

    public void Example()

    {

        UnitTestContext context = GetContext();

        BackgroundWorker worker = new BackgroundWorker();

        worker.DoWork += (o, e) =>

        {

            // Do some processing…

        };

        worker.RunWorkerCompleted += (o, e) =>

        {

        };

        worker.RunWorkerAsync();

 

        context.Complete();

    }

}

The subtle differences in this approach are simply that you’re using the UnitTestContext object to block the thread, or not, for you depending on the framework your test is running in as well as having an AsyncAsserter to manage getting exceptions back to the test thread for you.

In the first example you would end up in a deadlock in Silverlight and if your DoWork method threw an exception in either framework it would be interpreted as an unhandled exception and would cause your test to hang. With UnitDriven we are able to manage this easily by using an Assert.Try( … )

[TestMethod]

[ExpectedException(typeof(InvalidOperationException))]

public void ExpectedExceptionExample()

{

    UnitTestContext context = GetContext();

    BackgroundWorker worker = new BackgroundWorker();

    worker.RunWorkerCompleted += (o, e) =>

    {

        // catches exception here and passes to the context.

        context.Assert.Try(() =>

        {

            throw new InvalidOperationException();

        });

        context.Assert.Fail();

    };

    worker.RunWorkerAsync();

 

    // When the context is triggered it will find the

    // exception and re-throw it in .NET

    // and simply pass it back in Silverlight.

    context.Complete();

}

For some working examples check out our AsyncTests.cs on codeplex.

Happy Testing!

NVelocity Code Generation Tool for NBusiness

I’ve really been struggling with how to improve creating templates for NBusiness 3 so that it is much easier than it is now. I have been delayed by the prospect of actually creating any more templates with the CodeDom. It is just too painful and ugly.

On one hand I really want templates to be classes because this allows me to search for them easily, they are either in the same assembly or in a referenced assembly. It also allows me to easily gather meta-data about templates (attributes) and actually run the templates. It also allows for the template developer to generate code however they way, it’s very powerful. There also does not need to be some special path that you store templates in and there does not need to be a relative path specified in the entity. It’s a very light-weight and powerful way to do templates.
Unfortunately the problem with this approach is that your templates are probably going to end up being done with the CodeDom, which as you probably know is a royal pain in the butt. If you try to use a template engine such as NVelocity then you’re stuck taking care of the path issues yourself, which is ok but is also sort of annoying.
Well last night I finally figured out a great solution to this problem. I have decided to include an NBusinessTemplateCodeGenerator Custom Tool to the visual studio integration project. This custom tool will generate for you a template class, based on a template file it is applied to in your solution. Then your entities can reference that template and will use NVelocity to do the generation. I plan on making it extensible somehow so other template tools (such as codesmith?) could be used. Here are some screenshots of a working prototype.
So in this example my simple NVelocity template has the following code:
using System;
 
namespace $entity.Namespace
{
      public partial class $entity.Name
      {
#foreach($field in $entity.Fields)
            public $field.TypeName $field.Name { get; set; }
#end
      }
}
The .nbt (NBusiness Template) file will create a Template class such as this:
[NBusiness.Templates.CodeTemplateAttribute()]
public class TestTemplate : NBusiness.Templates.TemplateBase {
    protected override NBusiness.CodeDom.Compiler.EntityFile Compile(NBusiness.CodeDom.Entity entity, NBusiness.CodeDom.Compiler.EntityCompileParameters parameters) {
        NBusiness.Templates.ICodeGeneratorEngine engine = NBusiness.Templates.CodeGeneratorEngine.GetEngine();
        string template = System.Text.Encoding.UTF8.GetString(System.Convert.FromBase64String(@”IyoNCiAgICBTaW1wbGUgRW50aXR5SW5mbyBDb…you get the idea”));
        byte[] generated = engine.Generate(entity, template, parameters);
        return new NBusiness.CodeDom.Compiler.EntityFile(entity.Name, generated);
    }
}
…Which is ugly, which is why this tool was created. It will convert your template code into a base64 string which will be converted back into code before getting passed into the NVelocity engine and merged with the incoming entity. This class will now be accessible by entities in the same project or referencing projects.
Your entity file might look like this:
using ClassLibrary1;
 
family Test
{
      entity A as TestTemplate
      {
            field auto id int aid;
            field nullable string data;
            field nullable double value;
      }
}
Which when run through the TestTemplate will produce code that looks like this:
using System;
 
namespace Test
{
      public partial class A
      {
            public int aid { get; set; }
            public string data { get; set; }
            public System.Nullable<double> value { get; set; }
      }
}
So all you have to do to get NVelocity working with NBusiness entities and models will be to add a .nbt file to your project which (if done through the Add New Item dialog) will have the Custom Tool NBusinessTemplateCodeGenerator applied to it. Then author away!
I will be using this system to author the core templates that come with NBusiness from now on.