Devlico.Us
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @devlicious

Christopher Bennage

Our WPF book is now available!
follow @bennage on Twitter!
 


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?



Comments

Rob Eisenberg said:

My only thought is that we should add this to Caliburn, especially if you develop the fluent API you are thinking of. That would enable us to test that views are bound properly to models and that models are raising the notification necessary for proper binding as well.
# November 13, 2008 5:16 PM

DotNetKicks.com said:

You've been kicked (a good thing) - Trackback from DotNetKicks.com

# November 13, 2008 5:33 PM

KG2V said:

Not for the unit test, but for the property - don't raise a propertychanged event if the property is equal - ie do a compare first, and only change if you know, it actually changed
# November 13, 2008 6:45 PM

Samuel Jack said:

Interesting. I've just been thinking about the same problem over the last couple of weeks. I came up with a similar approach to testing that the correct propertychanged events get raised. As part of that, I developed an EventAsserter that makes it easy to assert that events are raised. I just blogged about a very simple version of it this morning, and I'll hopefully put up the full version before too long. See http://blog.functionalfun.net/2008/11/passing-around-references-to-events.html
# November 14, 2008 7:46 AM

Dew Drop - November 14, 2008 | Alvin Ashcraft's Morning Dew said:

Pingback from  Dew Drop - November 14, 2008 | Alvin Ashcraft's Morning Dew

# November 14, 2008 9:25 AM

Andreas B&#246;ckert said:

I fully share your opinion on "yet another string" in the code. The use of expression trees is very clever. I think that this approach could be utilized even more. Wouldn't it be possible to use the same approach when raising the event in the first place? Something along the lines of: set { SetAndRaise( () => this.FirstName = value ); } A bit of magic but perhaps the tradeoff between this and string literals is ok?
# November 18, 2008 2:14 AM

Christopher Bennage said:

@Andreas

I have done this before:

set{ _firstName=value; RaisePropertyChanged( ()=>FirstName);}

There a performance hit, but for many application I don't think it would matter.

Using a single expression to both set and raise the notification, I'm not sure that it can be done that way. I'd need to check.

# November 18, 2008 8:26 AM

Simon said:

perhaps use http://www.codeplex.com/classtester it has INotifyPropertyChanged support out of the box :)
# November 18, 2008 6:07 PM

Christopher Bennage said:

@Simon Thanks, but (perhaps unfortunately) I've already added everything to the Caliburn trunk. I'll post about it later in the week.

# November 18, 2008 6:13 PM

Simon said:

Pity I am at the moment pushing for classtester to make better use of generics and lambda expressions which will make using is much easier.
# November 18, 2008 6:49 PM

Specifying properties using Lambda expression trees instead of Reflection « ICoder said:

Pingback from  Specifying properties using Lambda expression trees instead of Reflection &laquo; ICoder

# November 28, 2008 1:29 PM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add

About Christopher Bennage

Christopher is a software developer and consultant at Blue Spire Consulting, a company he co-founded with Rob Eisenberg in 2006. He is a Christian, a marginal musician, and an armchair philosopher. His interests include programming, liberal education, science, truth, beauty, and a number of deceased British authors (C. S. Lewis, G. K. Chesterton, and most recently Owen Barfield.) He lives in Tallahassee, FL with his wife and three children and still prefers to play as the Night Elves in WarCraft 3. Check out Devlicio.us!

Our Sponsors

Proudly Partnered With