Runar Ovesen Hjerpbakk

Programmer. Software Architect. Technical Manager.

Testing ViewModels using INotifyPropertyChanged verifier

I enjoy working with the MVVM pattern which underpins Xamarin Forms, WPF, UWP and other XAML based application frameworks. Testability is one of the pattern’s strengths, and after reading about Spotify’s Microservices Testing Honeycomb I’ve come to reevaluate my earlier client testing strategies.

Read their article for details on testing microservices, but I argue that this approach is valuable also on the client-side. Luckily, the interaction points in XAML based apps using MVVM are well defined. The interaction points are the commands, the events and their effect on application state.

Testing commands is trivial, they’re only public methods after all, but what about properties and their INotifyPropertyChanged events? We want to make it easy to verify that the UI does what it should, using as few tests as reasonable in doing so.

Introducing NotifyPropertyChanged.Verifier, a fluent extension of xUnit for testing implementations of INotifyPropertyChanged in ViewModels.

tl;dr

vm.ShouldNotifyOn(vm => vm.PropertyWithNotify)
  .When(vm => vm.PropertyWithNotify = 42);

vm.ShouldNotNotifyOn(vm => vm.PropertyWithoutNotify)
  .When(vm => vm.PropertyWithoutNotify = -1);

Usage

Consider the following ViewModel:

public class ViewModel : INotifyPropertyChanged {
  int backingField;
  string backingField2;

  public int PropertyWithoutNotify { get; set; }

  public int PropertyWithNotify {
      get => backingField;
      set {
        backingField = value;
        OnPropertyChanged();
      }
  }

  public string PropertyWithMultipleNotifies {
      get => backingField2;
      set {
          PropertyWithNotify = int.Parse(value);
          OnPropertyChanged();
      }
  }

  public event PropertyChangedEventHandler PropertyChanged;

  private void OnPropertyChanged([CallerMemberName] string propertyName = "") =>
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

To test it, create an xUnit-test project and add a NuGet reference to NotifyPropertyChanged.Verifier. It’s a .Net Standard 2.0 library and can be used both in .Net Core and the full .NET Framework. The preceding ViewModel can test its implementation of INotifyPropertyChanged doing:

using NotifyPropertyChanged.Verifier;
using Xunit;

namespace Tests {
    public class UnitTests {
        readonly ViewModel vm;

        public UnitTests() => vm = new ViewModel();

        [Fact]
        public void PropertyWithNotify_WillRaiseNotifyEvent() =>
            vm.ShouldNotifyOn(vm => vm.PropertyWithNotify)
              .When(vm => vm.PropertyWithNotify = 42);

        [Fact]
        public void PropertyWithoutNotify_WillNotRaiseNotifyEvent() =>
            vm.ShouldNotNotifyOn(vm => vm.PropertyWithoutNotify)
              .When(vm => vm.PropertyWithoutNotify = -1);

        [Fact]
        public void PropertyWithMultipleNotifies_WillRaiseMultipleNotifyEvents() =>
            vm.ShouldNotNotifyOn(vm => vm.PropertyWithNotify,
                                 vm => vm.PropertyWithMultipleNotifies)
              .When(vm => vm.PropertyWithMultipleNotifies = "42");
    }
}

The library consists of two extension methods on INotifyPropertyChanged, ShouldNotifyOn and ShouldNotNotifyOn which takes 1 or more property expressions as input. These are the properties that should either receive or not receive a NotifyPropertyChanged-event when an Action<T> is called by the When method. This can anything, not only methods or properties on the ViewModel itself.

The UnitTests class here is made simple for the sake of example, but real ViewModel tests should test user scenarios as much as possible. That is, keep implementation detail tests to a minimum, and utilize NotifyPropertyChanged.Verifier in your integration tests to verify the flow of events. Strive to hide implementation complexity from your tests, making them robust against changes not affecting the user.

Inspired by this blogpost.