Christopher Bennage

Sponsors

The Lounge

Wicked Cool Jobs

Syndication

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Testing Bindings In WPF

I was inspired to get off my duff and blog today by a tweet from Scott Hanselman. I didn’t really dig into the context of his tweet, but it was about TDD and WPF. In case you haven’t figured it out, I’m a big fan of WPF and TDD. Luckily, I’ve had the opportunity to work on a number of varied WPF projects (most using TDD) over the last couple of years. One problem I experienced over and over was incorrect bindings in my Xaml (as I alluded to in this post). Binding errors are silent. They sneak up on you ninja-like. If you are keeping an eye on your Output window you can catch them, but it is very easy for them to just slip by.

Karate LessonsAbout two years ago, Rob and I were working on a student records application for the FSU College of Music. The system handled the entire lifecycle of a student, from application to graduation, and that involved lots of data. We had a number of screens in the app that were merely large data entry screens. Binding errors where a problem, and it seemed like a new batch showed up every time we made a release to the users.

The errors might have been the result of renaming a bound property, bad spelling, or some other such silliness. They were easy mistakes to make and thus I began to think that we really needed a way to automatically test them.

Well, I’m lazy and time passed (maybe a year) and then I mentioned it again to Rob. The next day he committed a prototype to the Caliburn trunk. (He’s like my own personal Ayende.)

Here’s an example test copied directly from Caliburn’s documentation:

public class TestBase
{
    static TestBase()
    {
        var app = new MyApplication();
        app.InitializeComponent();
    }
}
[TestFixture]
public class TestCase : TestBase
{
    [Test]
    public void Test()
    {
        var validator = Validator.For<MyUserControl, MyModel>();
        var result = validator.Validate();

        Assert.That(result.HasErrors, Is.False, result.ErrorSummary);
    }
}

In TestBase, we initialize the actual application so that we have access to application level resources. We need to this in a static constructor because we don’t want more than one instance of the application in the AppDomain.

Now, check out the Test() method. The static class Validator.For<T,K>() method returns an instance of BindingValidator<K>. The type T is some FrameworkElement that contains the bindings we’re interested in and K is the type that the FrameworkElement is bound to. Calling the Validate() method causes Caliburn to instantiate T, crawl it’s tree, and collect all the bindings. It then compares those bindings to the type K to see if they are valid. Validate() actually returns an instance of ValidationResult<K>, which is a summary of everything that Caliburn found while comparing the bindings against the actual type T.

The assertion in the test means that we expect Caliburn to find no errors, and if it does to provide us the summary. This is useful because there might be a number of binding errors and the ErrorSummary will give us the whole list.

At this point, I’m just repeat what’s stated in Caliburn’s documentation. NH Prof was the first project where we’ve actually used the Testability features of Caliburn. Let’s check out a real live test from it:

[TestFixture]
public class MessageBoxViewTextFixture : ViewTestFixtureBase
{
    private readonly ValidationResult<MessagePresenter> bindings;

    public MessageBoxViewTextFixture()
    {
        bindings = Validator.For<MessageBoxView, MessagePresenter>()
            .Validate();
    }

    [Test]
    public void BindingsDoNotHaveErrors()
    {
        Assert.That(bindings.HasErrors, Is.False, bindings.ErrorSummary);
    }

    [Test]
    public void MessageIsBound()
    {
        Assert.That(bindings.WasBoundTo(x => x.Message));
    }
}

This test is for the view that displays a simple popup message. We have a simple presenter that backs the view and it has a message to display in its property Message. It’s tested independent of the view. (This is nice because we have a case in NH Prof where a single presenter might be bound to multiple views.)

The ViewTestFixtureBase is identical to TestBase from the example above. Since it’s expensive to have Caliburn instantiate and crawl the UI validating the bindings, we only want to do this once. (I’m not really sure why I used the constructor, instead of [TestFixtureSetUp], but I did.)

The first test covers 90% of what you typically need, catching spelling errors, etc. The second test however explicitly checks to see if we are bound to the Message property on the presenter. This is useful because that’s the core reason for this view. There may be other properties bound, but this is the one we definitely care about. (I think this affords liberty to designers while ensuring that a view delivers the business value that was intended.)

Now, let’s pretend that I have a bad binding in my markup. Here’s the core snippet for the view used in the test above, MessageBoxView:

<Grid MaxWidth="260">
    <StackPanel>
        <TextBlock Foreground="{StaticResource Text}" 
                   FontSize="14"
                   TextWrapping="Wrap"
                   Text="{Binding Messyage}" />
        <Button Content="OK"
                Margin="4 8 4 4"
                HorizontalAlignment="Center"
                IsCancel="True" />
    </StackPanel>    
</Grid>

Oops, I misspelled Message in the binding! When I execute my tests, they both fail. The first because there is a binding error and the second because it can’t find Message. Here’s the actual exception from the first test:

NUnit.Framework.AssertionException:   [MessageBoxView.Grid.StackPanel.TextBlock] The property 'Messyage' was not found on 'MessagePresenter'.
  Expected: False
  But was:  True

Notice that it actually gives you the path to the offending binding: MessageBoxView.Grid.StackPanel.TextBlock. Now that’s just cool! Thanks Rob!

I can’t explain how much pain it saves to be able to do this. If you are doing TDD with WPF, you really need to check this out.

I’ll post over the weekend some tests that are a bit more BDD in their styles.


Posted 02-19-2009 10:56 PM by Christopher Bennage
Filed under: , , , , ,

[Advertisement]

Comments

Silverlight Travel » Testing Bindings In WPF wrote Silverlight Travel &raquo; Testing Bindings In WPF
on 02-20-2009 3:23 AM

Pingback from  Silverlight Travel &raquo; Testing Bindings In WPF

DotNetShoutout wrote Testing Bindings In WPF - Christopher Bennage
on 02-20-2009 9:49 AM

Thank you for submitting this cool story - Trackback from DotNetShoutout

David Roh wrote re: Testing Bindings In WPF
on 02-20-2009 11:11 AM

Thank you very much Christopher for posting this and especially the "UI Patterns for WPF"!

We would like to get you opinion, comments, sanity check of several conclusion that we have made -

We are producing software that controls medical equipment and cannot afford any errors (bugs, exception, logic error, etc.) - all coding errors must be identified either as compile time errors or with automated testing tools which in our case will probably have to be custom testing tools created especially for this project (we cannot have any - zero - runtime errors in our deliverables to QA).

We are building an embedded WPF equipment control application.

As a FDA monitored project we will be responsible for quality for any open source project that we include in our application so the learning curve, quality, size, complexity, active development are all areas of concern for us.

We have been looking at several libraries - Caliburn, Autofac, Composite Application Library, Ninject, and roll your own per Josh Smith, Marlon Grech, John Gossman.

Since we are not experts on these libraries and have not done any in depth evaluations, the following are mostly our impressions:

Composite Application Library - we will probably look at this for it's educational value but not attempt to use it because Microsoft's libraries of this type tend to be all things to all projects with a large learning curve and a large amount of code (we are not sure if this is valid impression of the CAL or not).

Caliburn - we like the capabilities and experience that has gone into the development of this library; however, we probably will not use it because it still has bugs being fixed, is not yet released, has dependencies upon Rhino Mocks and NUnit - since we must insure the quality of all the open source used in our project this means that the solution now becomes part of the problem.

Ninject - we really like the concept of small, fast, and easy to use; however, this is just basically a IoC framework without testing support which is essential for us - we may still try to use it and roll our own testing.

Autofac - seems to be very popular but it's a moving target which we do not know a lot about.

Roll-our-Own - we probably will use something along the lines of using Visual Studio 2008's built in test framework capability with a mediator type command structure using delegates or maybe Josh Smiths RelayCommands or Marlon Grech's mediator but with delegates in conjunction with the one project in the "UI Patterns for WPF" example code that you did not explain :-) (the 06 Commands project which seems to be a Supervising Controller with delegate commands).

Anyway, any guidance and/or correction of some of our perceptions would be deeply appreciated.

Thanks for the great "WPF in 24 Hours" book and for all your great post and examples that you have shared.

David Roh

Cheap soma. wrote Cheap soma.
on 03-05-2010 10:51 PM

Soma buy soma cheap soma soma online. Watson soma cheap. Cheap soma.

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)

CodeBetter.Com