Generic Extension Methods to Test INotifyPropertyChanged
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); }
//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.