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
Unit Testing Change Notification

In WPF work, it is very common to work with implementations of INotifyPropertyChanged. We need support for change notification in order to bind to the UI, and this interface is frequently the best approach. This means I have lots of properties that look like this:

public string FirstName
{
    get { return _firstName;}
    set
    {
        _firstName = value;
        RaisePropertyChanged("FirstName");
    }
}

I've gone back and forth as to whether or not I should test these properties for change notification on properties.  My main argument for not doing it, is the ROI. The typical test pattern might look something like this:

[Test]
public void FirstName_raises_change_notification()
{
    bool has_property_changed = false;
    _person.PropertyChanged += 
        (s, e) => { if (e.PropertyName == "FirstName") has_property_changed = true; };
    _person.FirstName = "new_name";

    Assert.That(has_property_changed);
}

Sure, this isn't a long test, but when you consider the number of properties I could be dealing with you'll see the potential for a lot of noise.

I also don't like the fact that I have yet another string in my code; easy to miss when refactoring. Nevertheless, I feel like I ought to have test coverage for these change notifications.

What's the solution? Make change notification easier to test.

Step One

I extracted out the test pattern into an extension method that applies to INotifyPropertyChanged. So my test now looks like this:

[Test]
public void FirstName_raises_change_notification()
{
    _person.AssertThatPropertyRaisesChangeNotification(
        () => _person.FirstName);
}

By using an expression to identify the property, I removed the magic string. I also have an overload if I need to set the property to a specific value:

[Test]
public void FirstName_raises_change_notification()
{
    _person.AssertThatPropertyRaisesChangeNotification(
        () => _person.FirstName, "new_name");
}

The code for the extension is this:

public static class AssertExtensions
{
    public static void AssertThatPropertyRaisesChangeNotification<T, K>(
        this T propertyOwner,
        Expression<Func<K>> property,
        K valueToSet) where T : INotifyPropertyChanged
    {
        var has_property_changed = false;

        var memberExpression = (MemberExpression) property.Body;
        var propertyInfo = (PropertyInfo) memberExpression.Member;

        propertyOwner.PropertyChanged +=
            (s, e) => { if (e.PropertyName == propertyInfo.Name) has_property_changed = true; };

        propertyInfo.SetValue(propertyOwner, valueToSet, null);

        if (has_property_changed) return;

        var msg =
            string.Format(
                "The property, {0}, did not raise change notification.\n" +
                "The property was set to '{1}'.\nThe property is owned by {2}",
                propertyInfo.Name,
                valueToSet,
                typeof (T).Name);

        throw new AssertionException(msg);
    }

    public static void AssertThatPropertyRaisesChangeNotification<T, K>(
        this T propertyOwner,
        Expression<Func<K>> property) where T : INotifyPropertyChanged
    {
        propertyOwner.AssertThatPropertyRaisesChangeNotification(property, default(K));
    }
}

Step Two

The next step (which I haven't done yet) would be to provide a fluent API for easily expressing which classes and properties I care about. With the extension above, I'm still just dealing with one property at a time.

What I would like is a way to say "all public properties on class X, except Y & Z" or perhaps "only properties Y & Z on class X". Then I can have concise, descriptive, intent-revealing tests regarding change notification.

Thoughts? Improvements?


Posted 11-13-2008 3:43 PM by Christopher Bennage
Filed under: , , ,

[Advertisement]

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
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)