$ cat "

Assertion Classes: Reuse and Expressiveness instead of Test Class Inheritance Hierarchies

"

If you have several classes which have some aspect in common, you probably have test classes that have similar tests as well. These similarities often lead to duplication that needs to be removed. One way to remove this duplication is to extract a common super class for the test classes, but inheritance can be a quite inflexible way of sharing behaviour between classes. For example, what happens when you have a test class that share behaviour with not only one other class, but two?

Due to the limitations of inheritance, composition is often a better choice. So, how can you use composition to share functionality between test classes? Let's look at an example from the world of Windows Presentation Foundation and MVVM.

Here is a typical view model class, which implements the interface INotifyPropertyChanged. If you are using MVVM you probably have quite a lot of classes implementing this interface.

public class ViewModel : INotifyPropertyChanged
{
private string _someProperty;

public string SomeProperty
{
get { return _someProperty; }
set
{
_someProperty = value;
RaisePropertyChanged("SomeProperty");
}
}

public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}

public event PropertyChangedEventHandler PropertyChanged;
}

Here is a simple test class, with a single test case that verifies that the property in the view model actually raises the PropertyChanged event when changed.

[TestFixture]
public class ViewModelTests
{
private ViewModel _viewModel;
private string _actualPropertyName;
private int _eventsRaised;

[SetUp]
public void SetUp()
{
_viewModel = new ViewModel();
_eventsRaised = 0;
_actualPropertyname = "";
}

[Test]
public void Setting_SomeProperty_raises_property_changed_event()
{
StartListeningForPropertyChangedEvent();

_viewModel.SomeProperty = "new value";

AssertSinglePropertyChangedEventWasFiredFor("SomeProperty");
}

private void StartListeningForPropertyChangedEvent()
{
_viewModel.PropertyChanged += (s, e) =>
{
_eventsRaised++;
_actualPropertyName = e.PropertyName;
};
}

private void AssertSinglePropertyChangedEventWasFiredFor(string expectedPropertyName)
{
Assert.AreEqual(1, _eventsRaised, "number of property changed events raised");
Assert.AreEqual(expectedPropertyName, _actualPropertyName);
}
}

Now imagine that you write another view model class. You will probably need to write test cases for that class that are almost identical to the test case above. You could extract the helper methods to a super class, but as mentioned above, that is not an ideal solution.

So, now to the good stuff. A simple way of cleaning up the test class above is to extract another class which handles everything that has to do with asserting that a property raises the property changed event.

public class ExpectPropertyChanged
{
private string _actualPropertyName;
private int _eventsRaised;
private string _expectedPropertyName;

private ExpectPropertyChanged(INotifyPropertyChanged sender)
{
sender.PropertyChanged += (s, e) =>
{
_eventsRaised++;
_actualPropertyName = e.PropertyName;
};
}

public static ExpectPropertyChanged On(INotifyPropertyChanged sender)
{
return new ExpectPropertyChanged(sender);
}

public ExpectPropertyChanged ForProperty(string expectedPropertyName)
{
_expectedPropertyName = expectedPropertyName;
return this;
}

public void During(Action action)
{
action.Invoke();

Assert.AreEqual(1, _eventsRaised, "number of property changed events raised");
Assert.AreEqual(_expectedPropertyName, _actualPropertyName);
}
}

This removes the need for the helper methods in the test class and enables you to use the same assertion code in any other test class without having to tie the classes together via a static type relationship. Here is the test class after the refactoring:

[TestFixture]
public class ViewModelTests
{
private ViewModel _viewModel;

[SetUp]
public void SetUp()
{
_viewModel = new ViewModel();
}

[Test]
public void Setting_SomeProperty_raises_property_changed_event()
{
ExpectPropertyChanged
.On(_viewModel)
.ForProperty("SomeProperty")
.During(() => _viewModel.SomeProperty = "new value");
}
}

So, why would you bother with this stuff? First of all, it let's you reuse code much easier than through inheritance. Apart from that it also simplifies your actual test classes since you can extract lots of boring helper methods into their own classes, which improves the signal to noise ratio significantly. Finally it also gives you a new level of expressiveness by naming the assertion class and introducing methods on the assertions which explain the meaning of each value used. This last point also solves the problem with composite assertions that take several parameters. By introducing an assertion class you can set each value using a method with a self documenting name, which makes it obvious what the value means, instead of having to try your best to guess what the third integer parameter does.

Written by Erik Öjebo 2010-10-28 22:38

    Comments