TDD: Helper for checking PropertyChanged event gets raised

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.

7 thoughts on “TDD: Helper for checking PropertyChanged event gets raised

  1. I use the following for testing events:

    bool wasCalled = false;
    viewModel.PropertyChanged += (s, e) => { wasCalled = true; }

    // Invoke action that raises event

    Assert.IsTrue(wasCalled);

  2. Ah. The viewmodel must also implement idisposable which would then in turn dispose of the event listener and make the assertion. For example:

    using (var viewModel = new ViewModel()) {
    viewModel.AssertRaisesPropertyChangedFor(“PackPath”);
    viewModel.OnFileSelected(@”C:\foo.zip”);
    }

  3. Yeah but you are only making your assertion in the dispose of your event listener. So your test while it will pass, won’t make the assertion unless you dispose of it in your test.

Comments are closed.