Today I am working on my first WPF app, using the WPF Model-View-ViewModel (MVVM) Toolkit. Naturally, we are using TDD -- like ASP.NET MVC, WPF ViewModels and ICommands lend themselves very nicely to unit testing, even around difficult dependencies like OpenFileDialog.

Anyway, one problem I am seeing repeated is writing tests for PropertyChanged events firing at the correct time. This is required so that WPF views can display updated values when something changes. For a test helper, I wrote a quick disposable event listener and extension method for this:

[TestMethod]
public void Should_raise_pack_path_property_changed_event()
{
    viewModel.AssertRaisesPropertyChangedFor("PackPath");
    viewModel.OnFileSelected(@"C:\foo.zip");
}

An assertion will fail if the PropertyChanged event does not fire with the correct property name. Here is the extension method:

public static class INotifyPropertyChangedExtensions
{
    public static void AssertRaisesPropertyChangedFor(this INotifyPropertyChanged obj,
        string propertyName)
    {
        new PropertyChangedEventListener(obj, propertyName);
    }
}

... and the event listener:

/// <summary>
/// Helper class for asserting a PropertyChanged event gets raised for a particular
/// property. If it hasn't been called by the time this object is disposed, an
/// assertion will fail.</summary>
public class PropertyChangedEventListener : IDisposable
{
    bool wasRaised = false;
    readonly string expectedPropertyName;
    bool IsDisposed = false;
    readonly INotifyPropertyChanged obj;
    public PropertyChangedEventListener(INotifyPropertyChanged obj, string propertyName)
    {
        if (obj == null)
            throw new ArgumentNullException("obj");
        if (propertyName == null)
            throw new ArgumentNullException("propertyName");
        this.obj = obj;
        this.expectedPropertyName = propertyName;
        obj.PropertyChanged += new PropertyChangedEventHandler(OnPropertyChanged);
    }
    void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (this.expectedPropertyName.Equals(e.PropertyName))
            this.wasRaised = true;
    }
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    protected void Dispose(bool Disposing)
    {
        if (!IsDisposed)
        {
            if (Disposing)
            {
                // Cleanup references...
                this.obj.PropertyChanged -= new PropertyChangedEventHandler(OnPropertyChanged);
                // Assert we got called
                Assert.IsTrue(this.wasRaised,
                    String.Format("PropertyChanged was not raised for property '{0}'",
                        this.expectedPropertyName));
            }
        }
        IsDisposed = true;
    }
    ~PropertyChangedEventListener()
    {
        Dispose(false);
    }
}

It's not fancy, and it's probably not thread-safe, but it does the trick for our app.