Dotneteers.net
All for .net, .net for all!

Using NUnit to test Windows Phone 7 applications – Part 2

VBandi's blog

Syndication

Subscribe

Generic Content

In the previous post of this series, we managed to get NUnit up and running to test WP7 code outside the Windows Phone emulator, in test runners such as NUnit, Resharper or Ncrunch. In this post, we pay the price for that as we move on to more complicated tests. I’ll focus on ObservableCollections and the INotifyPropertyChanged in this post.

ObservableCollection gotchas

Suppose we want to extend our calculator with a simple function that returns all positive integers until a given number. We start implementing the tests:

[Test]
public void PositivesUntil0ShouldBeEmpty()
{
    _calc.X = 0;
    Assert.AreEqual(0, _calc.PositiveIntsUntilX.Count);
}

Of course, this doesn’t compile yet, we need the X and the PositiveIntsUntilX properties. Since later we want the Calculator to act as a ViewModel, and bind directly to it, the PositiveIntsUntilX should be an ObservableCollection, to be recalculated whenever X changes. But we’re not there yet, just trying to do the simplest thing that will make the test pass. This should be it:

 

public int X { get; set; }

private ObservableCollection<int> _positivesUntilX = new ObservableCollection<int>();

public ObservableCollection<int> PositiveIntsUntilX
{
    get { return _positivesUntilX; }
}

And the test fails! More interestingly, it fails when the ObservableCollection is being created. But why?

image

The error message says: SetUp : System.IO.FileNotFoundException : Could not load file or assembly 'System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e' or one of its dependencies. The system cannot find the file specified.

The reason for this error is that the unit test runner cannot load the Windows Phone System.Windows dll, and thus throws errors as soon as that dll is referenced.

Now, you may want to follow Andriy Buday’s hints, and create a copy of your project, linking files to it, and use the real Silverlight runtime at this point. I will examine a different approach – less powerful, but simpler.

NUnit is confused since it cannot load the dll hosting the ObservableCollection class. But do we really need the ObservableCollection just to make sure the list has the right items? Can’t we just use a simple List<int> object here for testing, and an ObservableCollection in production?

Turns out, we can. Both ObservableCollection and List implement the IList interface. So, let’s change our code:

private IList<int> _positivesUntilX = General.IsTesting ? (IList<int>) new List<int>() : new ObservableCollection<int>();
public IList<int> PositiveIntsUntilX
{
     get { return _positivesUntilX; }
}
We will also need to create a new global class called General, that lets our code know if it is running in a test environment. This is not nice, and smells, but we’ll take care of that later.
public class General
{
    public static bool IsTesting = false;
}

The Windows Phone Silverlight runtime will still update the ListBox bound to PositiveIntsUntilX, since it detects that there is an ObservableCollection (more precisely, an object implementing the INotifyCollectionChanged interface) behind the ILIst property. And our unit tests never need to create an ObservableCollection, so they pass.

Comparing Lists

Next, we want to make sure that the PositiveIntsUntilX list has the right elements for different X values. No special WP7 issues here, but I thought it would be nice to have this here as well… It is useful to have a CompareLists<T> method for these tests. I put the CompareLists method in a TestBase class (which will also be useful later):

public class TestBase
{
    public void CompareLists<T>(IList<T> expected, IList<T> actual)
    {
        Assert.AreEqual(expected.Count, actual.Count);
        for (int i = 0; i < expected.Count; i++)
            Assert.AreEqual(expected[i], actual[i]);
    }
}

Here is the test:

[Test]
public void VerifyPositivesUntil11()
{
    _calc.X = 11;
    CompareLists(new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, _calc.PositiveIntsUntilX);
}

And of course, we have to change the implementations of our Calculator to make the test pass:

public int X
{
    get { return _x; }
    set
    {
        _x = value;
        CalculatePositivesUntilX();
    }
}

private IList<int> _positivesUntilX = General.IsTesting ? (IList<int>) new List<int>() : new ObservableCollection<int>();
private int _x;

public IList<int> PositiveIntsUntilX
{
    get { return _positivesUntilX; }
}

private void CalculatePositivesUntilX()
{
    _positivesUntilX.Clear();
    for (int i = 1; i <= X; i++)
        _positivesUntilX.Add(i);
}

All green!

Verifying that INotifypropertyChanged gets called

Most of the time you will have a central, proven mechanism for handling INotifyPropertyChanged in your properties. But sometimes you want to make sure in your tests that these actually get called at the right time. In our little example, we want to make sure that after setting X, the PropertyChanged even is raised for X, but not or PostitiveIntsUntilX – we went through all that trouble making it an ObservableCollection after all…

Here is the PropertyChanged notification watcher for the TestBase:

[SetUp]
public void TestBaseSetup()
{
    General.IsTesting = true;
    if (_propertyNotifications != null)
        foreach (var key in _propertyNotifications.Keys)
            StopMonitoringINotifyPropertyChanged(key);

    _propertyNotifications = new Dictionary<INotifyPropertyChanged, List<string>>();
}
protected Dictionary<INotifyPropertyChanged, List<string>> _propertyNotifications; public void MonitorINotifyPropertyChanged(INotifyPropertyChanged vm) { vm.PropertyChanged += OnVMOnPropertyChanged; } public void StopMonitoringINotifyPropertyChanged(INotifyPropertyChanged vm) { vm.PropertyChanged -= OnVMOnPropertyChanged; } private void OnVMOnPropertyChanged(object sender, PropertyChangedEventArgs args) { var key = (INotifyPropertyChanged) sender; if (!_propertyNotifications.ContainsKey(key)) _propertyNotifications.Add(key, new List<string>()); _propertyNotifications[key].Add(args.PropertyName); } public bool HasPropertyChangedBeenRaised(INotifyPropertyChanged vm, string propertyName) { return _propertyNotifications.ContainsKey(vm) && _propertyNotifications[vm].Contains(propertyName); }

The NUnit test runner executes the SetUp of the TestBase class first, so all is taken care of by the time we get to running the actual tests. Here is how to test if a PropertyChanged has been raised for our calculator:

[Test]
public void PropertyChangedNotRaisedForX()
{
    MonitorINotifyPropertyChanged(_calc);
    _calc.X = 10;
    Assert.IsTrue(HasPropertyChangedBeenRaised(_calc, "X"));
}

Of course the above test passes until you actually add the code to raise the event. I’ll leave that to you so that we can focus on the testing now.

If you want even more detailed verification, you can access all the events in the order they occurred by accessing the _propertyNotifications[_calc] list. This contains all the property names in the order their respective PropertyChanged even has been raised.

In the next post, I’ll look at the challenges of using Laurent Bugnion’s MVVM Light Toolkit with this Unit testing approach. I’ll give directions on how to handle his IsInDesignMode and Messenger while testing, and also a look at how some of the code smells above can be decreased by using the SimpleIOC container in the Toolkit.


Posted Aug 12 2012, 06:11 PM by vbandi
Filed under:

Comments

Using NUnit to test Windows Phone 7 applications ??? Part 2 wrote Using NUnit to test Windows Phone 7 applications ??? Part 2
on Mon, Aug 13 2012 14:14

Pingback from  Using NUnit to test Windows Phone 7 applications ??? Part 2

Dew Drop – August 13, 2012 (#1,383) | Alvin Ashcraft's Morning Dew wrote Dew Drop &ndash; August 13, 2012 (#1,383) | Alvin Ashcraft&#039;s Morning Dew
on Mon, Aug 13 2012 14:57

Pingback from  Dew Drop – August 13, 2012 (#1,383) | Alvin Ashcraft's Morning Dew