The project that I’m currently participating in is using Prism’s event mechanism. It allows communication between loosly coupled components by means of events. This article is about how you can unit test event handlers for such Prism events. First of all, here is a subscriber that subscribes for the ChildAddedEvent:
public SubjectUnderTest(IEventAggregator eventAggregator) { eventAggregator.GetEvent<ChildAddedEvent>().Subscribe(OnChildAdded); } private void OnChildAdded(ChildAddedEvent evt) { int var = 0; }
Crucial question
How can you now unit test this private OnChildAdded method?
My personal basic condition when writing a unit test is that every custom type used by subject under test is mocked. That way, the test coverage metrics get more valueable and the unit test is used for the purpose it is intended for.
A first attempt
You could call OnChildAdded in the unit test by means of PrivateObject class (Microsoft.VisualStudio.TestTools.UnitTesting):
// Act PrivateObject exposedSubjectUnderTest = new PrivateObject(subjectUnderTest); exposedSubjectUnderTest.Invoke("OnChildAdded", childAddedEvent);
However, let’s look for a better idea because the main issue here is that the “glue” between the subscription and the handler is not tested:
By the way, the green markers are comming from NCrunch and denotes the lines that are covered by only passing unit tests. There are other colors too: Red means that the line is covered but there is at least one failing unit test. Black marks a line not coverred by any unit test.
A better solution
Therefore I implemented the static EventHelper.SetupEventSubscription method that can be used in our unit tests. The key pieces are the callback parameter of type Action<Action<T>> and Moq’s Callback method. The type Action<Action<T>> is required because we want to make the delegate be available in the unit test for execution and an out parameter can’t be used inside a lambda expression.
public static void SetupEventSubscription<T>(IEventAggregator eventAggregator, out T evt, Action<Action<T>> callback) where T : PubSubEvent<T>, new() { evt = Mock.Of<T>(); SetupEventCallback(eventAggregator, evt, callback); } private static void SetupEventCallback<T>(IEventAggregator eventAggregator, T evt, Action<Action<T>> callback) where T : PubSubEvent<T>, new() { Mock.Get(eventAggregator).Setup(m => m.GetEvent<T>()).Returns(evt); Mock.Get(evt) .Setup(m => m.Subscribe(It.IsAny<Action<T>>(), It.IsAny<ThreadOption>(), It.IsAny<bool>(), It.IsAny<Predicate<T>>())) .Callback<Action<T>, ThreadOption, bool, Predicate<T>>((eventHandler, x, y, z) => callback(eventHandler)); }
Then, SetupEventSubscription method can be used like it is shown in the following code snippet. The code also shows a good use of PrivateObject: I used it in the SetChild extension method (line 11) for setting one of ChildAddedEvent’s private fields. Of course, SetChild is only visible in the unit tests.
// Arrange ChildAddedEvent childAddedEvent; Action<ChildAddedEvent> childAddedEventHandler = null; var eventAggregator = Mock.Of<IEventAggregator>(); EventHelper.SetupEventSubscription(eventAggregator, out childAddedEvent, childAddedEventHandler); var subjectUnderTest = new SubjectUnderTest(eventAggregator); var child = Mock.Of<IChild>(); childAddedEvent.SetChild(child) // Act childAddedEventHandler(childAddedEvent);
If somebody now removes the subscription, the unit test will fail. In addition, a renaming of the handler will also not require to update any unit test.