Friday, September 24, 2010

Getting more out of unit testing in Silverlight

As I already mentioned in my previous post, we're building a line-of-business app using Silverlight 4 and WCF Data Services. I really think Silverlight is great for those kind of systems, but since it is based on a different run-time as the full .NET framework, you may wonder what you'll run into when practicing TDD, relying on IoC or using a mocking framework.

Well, both Rhino Mocks and MOQ have a Silverlight version, although I have only used Rhino Mocks myself. Additionally, if you're into IoC containers like me, Microsoft Unity 2.0 has been working perfectly under Silverlight 4. Here are some additional considerations.

In general, I prefer the Arrange-Act-Assert style for state-based tests such as when specifying the behavior of domain entities or simple framework elements. However, for view models or other code that has a more orchestrational nature I prefer to use a style that more closely resembles Behavior Driven Design. Consider for example the specifications for a MVVM class.

    [TestClass]

    public class ManageRegistrationViewModelSpecs

    {
        [TestClass]

        public class When_adding_an_importing_country : SpecificationContext

        {

            private ManageRegistrationViewModel model;

            private Country countryToAdd;

 

            protected override void EstablishContext()

            {

                countryToAdd = new CountryBuilder().Build();

 

                model = new ManageRegistrationViewModelBuilder()

                    .WithCountry(countryToAdd)

                    .Build();

            }

 

            protected override void Because()

            {

                model.SelectedCountry = countryToAdd;

                model.AddCountry();

            }

 

            [TestMethod]

            public void It_should_add_a_new_importing_country_to_the_model()

            {

                model.ImportingCountries.Should().HaveCount(1);

 

                ImportingCountryModel countryModel = model.ImportingCountries.Single();

                countryModel.Country.Should().BeSameAs(countryToAdd);

                countryModel.LicenseType.Should().BeNull();

                countryModel.RegulatoryAgent.Should().BeNull();

                countryModel.MarketingAuthorizationHolder.Should().BeNull();

                countryModel.TradeName.Should().BeEmpty();

            }

 

            [TestMethod]

            public void It_should_queue_the_coresponding_command()

            {

                model.PendingChanges.Should().HaveCount(1);

                model.PendingChanges.Should()
                .ContainItemsAssignableTo<AddImportingCountryCommand>();

 

                var command = (AddImportingCountryCommand)model.PendingChanges.Single();

                command.CountryId.Should().Be(countryToAdd.Id);

            }

 

            [TestMethod]

            public void It_should_remove_that_country_from_the_list_of_available_countries()

            {

                model.Countries.Should().NotContain(countryToAdd);

            }

 

            [TestMethod]

            public void It_should_clear_the_selected_country()

            {

                model.SelectedCountry.Should().BeNull();

            }

        }

    }

Unfortunately, the current Silverlight Unit Test runner does not show the name of the outer test class, in this case ManageRegistrationViewModelSpecs, and I haven't found a solution for this yet. So if your list of tests is extensive, you may have some trouble finding the test your interested in. However, one nice trick I found by inspecting the source code of the test runner is that you can apply an [Exclusive] attribute to a particular class. This ensures that the test runner only runs that particular test. For completeness, here is the code of the SpecificationContext base-class I use:

    [TestClass]

    public class SpecificationContext

    {

        private Exception initializationException;

 

        [TestInitialize]

        public void TestInitialize()

        {

            try

            {

                EstablishContext();

                Because();

            }

            catch (Exception exception)

            {

                initializationException = exception;

            }

        }

 

        [TestMethod]

        public void Establish_context_and_because()

        {

            if (initializationException != null)

            {

                throw initializationException;

            }

        }

 

        [TestCleanup]

        public void TestCleanup()

        {

            CleanupContext();

        }

 

        protected virtual void EstablishContext() {  }

 

        protected virtual void Because() { }

 

        protected virtual void CleanupContext() { }

    }

You may notice the awkward exception handling code in there. I discovered that the current test runner swallows all exceptions that occur in the method marked with the [TestInitialize] attribute. That's why I explicitly catch every exception that occurs while calling EstablishContext and Because and re-throw it in a dedicated method marked with the [TestMethod] attribute. The full .NET Framework does not have this particular symptom, so It's a Silverlight thingy.

Another tip to improve the testing experience is to disable the tag editor that you get when you launch your test project. Replacing the Application_Startup method of the App class with the following lines quiets it down permanently:

    private void Application_Startup(object sender, StartupEventArgs e)

    {

        var settings = UnitTestSystem.CreateDefaultSettings();

        settings.StartRunImmediately = true;

 

        RootVisual = UnitTestSystem.CreateTestPage(settings);

    }

Also, if you apply the Continuous Integration practice, you may want to check out StatLight. I have not managed to get it running myself, but it is supposed to support running Silverlight tests from the command-line or from your favorite build engine.

And finally, if you're really into the testability and object-oriented design principles, you must check out Caliburn Micro. It's an open-source framework for Silverlight 4, Windows Phone 7 and WPF, and has some excellent concepts for relieving the burden of working with asynchronous code. I've been using it for over two months now and I'm exhilarated about its coroutine concept and the way you use it in your tests.

Monday, September 20, 2010

Silverlight Cookbook: WCF Data Services and NHibernate

This post is part of a series of blog posts detailing various aspects of the Silverlight Cookbook, an initiative to demonstrate proper design choices for building enterprise-class line-of-business applications with Silverlight (and WPF if you will). It currently consists out of the following parts.

In the Silverlight Cookbook I use a variation of the CQRS pattern where business-oriented commands are used to create, update or change the data, while WCF Data Services (formally ADO.NET Data Services) is used as a general-purpose query service.

Although WCF Data Services has been designed with the Entity Framework in mind, my desire to use an advanced ORM with features like 2nd level caching has let me to Shawn Wildermuth's attempt to support NHibernate as a WCF Data Service provider. Unfortunately, that solution no longer works with NHibernate 3.x, but I managed to get it working again by compiling my own version. But regardless of all the goodness of NHibernate 3, not everything went as smooth as I hoped for.

For starters, you need to expose all entities as entity sets even though you use them only in associations with another parent entity and never query on them. Consider for instance that we have a Recipe entity that has an aggregate relationship with one or more Rating entities. We'll never query directly on the ratings, but we still need to expose them like this:

    public class RestContext : NHibernateContext 
    { 
        public IQueryable<Recipe> Recipes 
        {
             get { return Session.Query<Recipe>();  }
        }

        public IQueryable<Rating> Ratings
        {
            get { return Session.Query<Rating>(); }
        }
    }

Another thing is that WCF Data Services chokes on the proxies that NHibernate generates at run-time. So if your entity lazy-loads another associated entity, you have to disable lazy-loading altogether. I've actually spent quite a few hours on using Reflector to investigate whether the NHibernateContext or DataService classes could be tweaked to properly map the derived proxy class back to the appropriate entity type, but failed. As a workaround, you either need to to disable lazy-loading in the mapping, or eagerly fetch the associations using the Fetch extension method introduced in NHibernate 3: 

   
public class RestContext : NHibernateContext
    {
       public IQueryable<Recipe> Recipes
        {
            get
            {
                return Session
                    .Query<Recipe>()
                    .Fetch(r => r.Ratings);
          }
        }
    }

Unfortunately, that introduces another 'feature' of NHibernate. I've never quite understood why, but when NHibernate fetches one-to-many and many-to-many associations, you end up with duplicate records in the final result set. Just appending a Distinct() to the entity set getter such as the one above doesn't help. This has actually cost me a lot of time to work out, because when loading a lot of entities, the result set ended up to be too big for WCF Data Services to transfer. You'll find the solution in a hidden feature of NHibernate.Linq that allows you to add a custom action to an IQueryable expression tree like this:

    queryable.QueryOptions.RegisterCustomAction(
        c => c.SetResultTransformer(new DistinctRootEntityResultTransformer()));

Since I needed to use this in many places and it obscures the intention of my code, I decided to wrap it in an extension method:

    public static class NHibernateQueryableExtensions
    {
        public static IQueryable<TEntity> DistinctDeferred<TEntity>(this IQueryable<TEntity> queryable)
        {
            var nHibernateQueryable = queryable as INHibernateQueryable<TEntity>;

            if (nHibernateQueryable != null)
            {
                nHibernateQueryable.QueryOptions.RegisterCustomAction(
                    c => c.SetResultTransformer(new DistinctRootEntityResultTransformer()));
            }

            return queryable;
        }
    }

It can be used just like the other extension methods applicable to IQueryable:

    public IQueryable<Registration> Registrations
    {
        get
        {
            return Session
                .Linq<Registration>()
                .Expand("Product")
                .DistinctDeferred();
        }
    }

Since I don't do any updates through the query service, I haven't tried how well CRUD operations through WCF Data Services work. However, I did notice that in order to receive the latest changes from the server, I have to make sure to set the MergeOption property of the generated DataServiceContext subclass to MergeOption.OverwriteChanges:

    queryContext = new QueryContext(queryServiceUri)
    {
        MergeOption = MergeOption.OverwriteChanges
    };

In the default setting, MergeOption.AppendOnly, it will assume all changes are made by the client, and ignore anything that occurred on the server (beats me why it does that). You can also use MergeOption.NoTracking and get the same behavior, but with MergeOption.OverwriteChanges, two queries with the same key (identified by the [DataServiceKey] attribute) will return the same physical object. This is quite convenient when binding a ComboBox to the list of available entities and the currently selected one.

To conclude this post, let me tell you that debugging WCF Data Services is definitely cumbersome. When something goes wrong or is incorrectly configured you'll only receive a generic error like this: 

clip_image0014 
You can solve that by slapping a [ServiceBehavior] attribute to your data service class like this:

    [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
    public class RestService : DataService<RestContext> { }

Or you can add the corresponding configuration setting to the <behaviors> element of your web.config:

<serviceBehaviors>
    <behavior name="">
        <serviceMetadata httpGetEnabled="true" />
        <serviceDebug includeExceptionDetailInFaults="true" />
    </behavior>
</serviceBehaviors>

Finally, you can force WCF Data Services to include a bit more info on the exception that occurred by changing the UseVerboseErrors property of the DataServiceConfiguration class. 

   public class QueryService : DataService<QueryContext>
    {
        public static void InitializeService(DataServiceConfiguration config)
        {
            config.UseVerboseErrors = true;

The next time you make a mistake or some down-level query operation throws an exception, you end up with something like this:

clip_image0024

Monday, September 13, 2010

Commercial support for the C# 3.0/4.0 Coding Guidelines

A few weeks ago I had a chat with Paul Jansen, CEO of a small Eindhoven-based company named Tiobe. You might indirectly know Tiobe for its programming language index and their commercial code checker ClockSharp. But they have also been hosting a C# Coding Standard I co-wrote for Philips Medical Systems for over seven years. He mentioned that it is still downloaded 4000-5000 times per month. However, he recently received some remarks that the current standard is rather outdated and was pointed at my efforts at www.csharpcodingguidelines.com.

He explained me their ideas to redesign their commercial code checker using parts of StyleCop and to support as many guidelines as possible from my own coding guidelines. Since we have been trying to do something similar using FxCop and StyleCop, and we know how difficult that is, I'm quite happy with that. In addition to that they'll start hosting my guidelines on their side, hopefully increasing its visibility. The only thing I need to do is to maintain the document, which I'm already doing, so that's no big deal.

Because most of their clients are in the technical automation or embedded systems profession, one thing we still need to figure out is how to deal with their desire to re-add some guidelines that I removed to keep the document compact. Maybe I'll maintain two versions of the document, one light version on www.csharpcodingguidelines.com and one more complete version on www.tiobe.com.

Thursday, September 02, 2010

The AI of ReSharper

I was just typing this…

image

…when ReSharper 5 suggested its “Loop can be converted into LINQ-expression”:

image

You gotta love this!