Generic Extension Methods to Test INotifyPropertyChanged
November 22nd, 2008
Just two extension methods I wrote in order to simplify testing of classes that implement INotifyPropertyChanged.
The first one just checks that the PropertyChanged event fires once and for the specified property:
/// <summary> /// Checks whether a given action produces a change event for /// a given property. /// </summary> /// <typeparam name="T">A class that implements <see cref="INotifyPropertyChanged"/>. /// </typeparam> /// <param name="host">The object that contains the changed property.</param> /// <param name="changeAction">An action that triggers the /// <see cref="INotifyPropertyChanged.PropertyChanged"/> event.</param> /// <param name="propertyName">The name of the changed property, as advertised by the /// <see cref="INotifyPropertyChanged.PropertyChanged"/> event.</param> public static void AssertFiresPropertyChangedEvent<T> (this T host, string propertyName, Action<T> changeAction) where T : INotifyPropertyChanged { bool fired = false; //register handler with assertion PropertyChangedEventHandler handler = (sender, e) => { fired = true; Assert.AreEqual(propertyName, e.PropertyName); }; //register listener, trigger action, then deregister host.PropertyChanged += handler; changeAction(host); host.PropertyChanged -= handler; Assert.IsTrue(fired); }
Now in order to run my test, I can write something like this as long as I’m not having multiple threads that change my object under test:
//create new user User user = new User("Tim"); //make sure changing the name raises event user.AssertFiresPropertyChangedEvent("Name", u => u.Name = "Tom");
The second extension method makes sure the event is fired a given number of times for a specified property. Apart from the obvious one (looping a few times), this one has additional use cases:
- I can test that the event doesn’t fire at all by setting the expected number of events to 0.
- This method ignores events for other properties, whereas the snippet above fails.
/// <summary> /// Checks whether a given action produces a specified number of property /// change events for a given property.<br/> /// This event listener just ignores event for properties other than /// <paramref name="propertyName"/>. /// </summary> /// <typeparam name="T">A class that implements <see cref="INotifyPropertyChanged"/>. /// </typeparam> /// <param name="host">The object that contains the changed property.</param> /// <param name="numberOfEvents">The expected number of change events for /// the specified property</param> /// <param name="changeAction">An action that triggers the /// <see cref="INotifyPropertyChanged.PropertyChanged"/> event.</param> /// <param name="propertyName">The name of the changed property, as advertised by the /// <see cref="INotifyPropertyChanged.PropertyChanged"/> event.</param> public static void AssertPropertyChangedEventCount<T>(this T host, string propertyName, int numberOfEvents, Action<T> changeAction) where T : INotifyPropertyChanged { int eventCount = 0; //register handler with assertion PropertyChangedEventHandler handler = (sender, e) => { //increment if the event refers to the tested property if (e.PropertyName == propertyName) eventCount++; }; //register handler, trigger action, then deregister host.PropertyChanged += handler; changeAction(host); host.PropertyChanged -= handler; //compare counted events with expected number Assert.AreEqual(numberOfEvents, eventCount); }
Update:
If you want to test a full set of properties of a class at once, check out Josh Twist’s ClassTester: This handy little library simplifies things remarkably when it comes to testing complete classes or even assemblies.
Have you seen the Automatic Class Tester?
http://www.codeplex.com/classtester
You can just point at your classes and it will test all properties. Classes that implement INotifyPropertyChanged can be automatically tested to see if they fire the appropriate events.
Where do I sign? 😉 Awesome work, thank you.
using your code for WP7 testing and loving it.
thanks!