Unit testing Caliburn applications in NUnit
This is part three in my series on Caliburn, if you haven't please read Part 1: Getting started with Caliburn and Part 2: Multiple Views on one ViewModel.
In this post I will look into the unit testing features of Caliburn and try to give a quick introduction to the essentials of what you need to know to write a couple of small unit tests in NUnit. The documentations on caliburn.codeplex.com on unit testing is fairly good, so I will only try to fill in some missing pieces and give a quick start guide to unit testing Caliburn. If or when you bump into problems with the simple setup described here, it's time to move over to more detailed documentation available in both the documentation and discussion forum on Codeplex.
Note: The examples described herein will be based on the sample project created in Part 1 and Part 2.
There are basically two things I would always like to add unit tests for in all my Caliburn applications:
- Verification of all data bindings between the View and the ViewModel
- Verification of all NotifyOfPropertyChanged
NUnit setup
I will show examples of both, but before we can start writing unit tests in NUnit we need to make sure NUnit operates on the STA thread. I have allready written a small post on this, but add it here as well as it is a necessary step to enable NUnit to operate correctly on our application.
We first create an empty project in our solution, and add an app.config with the following content:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="NUnit">
<section type="System.Configuration.NameValueSectionHandler"
name="TestRunner"></section>
</sectionGroup>
</configSections>
<NUnit>
<TestRunner>
<add value="STA" key="ApartmentState"></add>
</TestRunner>
</NUnit>
</configuration>
If we don't add this configuration we will get the following error when running out unit tests:
The calling thread must be STA
and NUnit will not execute our test. But once this is in place, we can write our first small and simple unit tests.
Verifying data bindings
The Caliburn.Testability contains the necessary code to allow us to add small verification tests on both that data was bound, and that data was not bound (Plus an extensive set of other features which I will not focus on this in this post). For my NumberInputView and NumberInputViewModel, I have wirtten two test methods, one for testing that all data bindings present between our View and ViewModel are valid, and one for test that spesific databindings are both present (and also not present for other properties)
using Caliburn.Testability;
using CaliburnCalculator.ViewModels;
using CaliburnCalculator.Views;
using NUnit.Framework;
namespace CaliburnCalculator.TestCaliburnCalculator
{
[TestFixture]
public class NumberInputTest
{
[Test]
public void TestGeneralDataBound()
{
var validator = Validator.For<NumberInputView, NumberInputViewModel>();
var result = validator.Validate();
Assert.False(result.HasErrors, result.ErrorSummary);
}
[Test]
public void TestSpesificBindings()
{
var validator = Validator.For<NumberInputView, NumberInputViewModel>();
var result = validator.Validate();
Assert.True(result.WasBoundTo(x => x.Number));
Assert.True(result.WasNotBoundTo(x => x.NumberNotBound));
}
}
}
Note: I have also an identical property to Number, called NumberNotBound, just to show how to verify that a property present in the ViewModel is not bound to the View.
There are only two lines of code here to do the actual validation on the databindings between my NumberInputView and my NumberInputViewModel, namely:
var result = new NumberInputViewModel(null); result.AssertThatAllProperties().RaiseChangeNotification();
This will do the validation, and return a ValidationResult which will indicate if there are errors in the databanding and enable me to test the databindings present, as described in the test method TestSpecificBindings. Caliburn.Testability also has support for resource dependency validation, but I will not look into that feature here.
Testing NotifyOfPropertyChanged
Note that these features are only available in WPF! With the change notification there are a couple of things worth testing:
- Check that all NotifyOfPropertyChanged calls are spelled correctly
- That a specific property raises notification of change
Given my NumberInputViewModel from my second tutorial:
using Caliburn.Core.Metadata;
using Caliburn.PresentationFramework.ApplicationModel;
using CaliburnCalculator.EventAggregator;
using CaliburnCalculator.Views;
namespace CaliburnCalculator.ViewModels
{
public interface INumberInputViewModel: IPresenter
{
}
[Singleton(typeof(INumberInputViewModel))]
[View(typeof(NIV2), Context = "Ctx2")]
[View(typeof(NumberInputView), Context = "Ctx1")]
public class NumberInputViewModel: MultiPresenter, INumberInputViewModel
{
private readonly IEventAggregator _eventAggregator;
public NumberInputViewModel(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
Number = 5;
}
private string numberStr;
public string NumberStr
{
get { return numberStr; }
set
{
numberStr = value;
NotifyOfPropertyChange("NumberStr2");
NotifyOfPropertyChange("CanSquareIt");
}
}
private int number;
public int Number
{
get { return number; }
set
{
number = value;
NumberStr = value.ToString();
NotifyOfPropertyChange("Number");
NotifyOfPropertyChange("CanSquareIt");
}
}
public bool CanSquareIt
{
get
{
int result;
return int.TryParse(NumberStr, out result);
}
}
public void SquareIt()
{
_eventAggregator.Publish(Number);
}
}
}
My NumberStr property is a good target for my first unit test on notification, as I have both a notification on the NumberStr change, as well as re-calculation on CanSquareIt, meaning, wether the SquareIt button should be enabled or not.
To test that all properties raise change notification, I can use an extension method to test that all properties raise at least one correct change notification as seen in this example:
using Caliburn.Testability.Extensions;
using CaliburnCalculator.ViewModels;
using NUnit.Framework;
namespace CaliburnCalculator.Tests
{
[TestFixture]
public class TestNotificationOnNumberInputView
{
[Test]
public void CheckAllChangenNotifications()
{
var result = new NumberInputViewModel(null);
result.AssertThatAllProperties().RaiseChangeNotification();
}
}
}
Note that I add the extension methods through the "using Caliburn.Testability.Extensions". If I run this now, I will get a green light, but if I comment out or change the text in the following line in the NumberInputViewModel:
NotifyOfPropertyChange("NumberStr");
I will get a red light on my unit test, as this property will no longer raise a notification changed event that it has changed (Only that it should re-calculate CanSquareIt). So by adding this small unit test I actually start to get an overview that my ViewModel contains the necessary notifications.
This is all good, but what if I have a property that does not and should not raise a change notification? What if my Number property was not raising any change notification? I can easily exclude properties for the check with the following extra:
[Test]
public void CheckAllChangeNotifactionsExampleTwo()
{
var result = new NumberInputViewModel(null);
result.AssertThatAllProperties().Ignoring(x => x.Number).RaiseChangeNotification();
}
Once again very sweet, but what if I have a bunch on properties that should not raise change notification, and just a few that should? Then I can easily just add check for specific properties like this:
[Test]
public void CheckAllChangeNotifactionsExampleThree()
{
var result = new NumberInputViewModel(null);
result.AssertThatProperty(x => x.Number).RaisesChangeNotification();
}
This is as far as I will dive into the testing features of Caliburn in post.
Note: This blog post is very similar to the tutorials present on caliburn.codeplex.com and is meant to be seen in combination with my other turorials on Caliburn. The intention is to give you a walk through to get started with Caliburn.
How to succeed with WPF and Silverlight (Slides
Running NUnit in an STA thread
Getting started with Caliburn Part 2: Multiple Views
Getting started with Caliburn
The future of Caliburn. Is Caliburn dead?
RSSReader example code from my NNUG speech (WPF
Sending parameters with Caliburns ActionMessage
Splash screen in WPF
Why use Custom Control instead of User Control
Log4Net for noobs