I spent some time recently working with Microsoft Composite Application Guidance (A.K.A. "Prism", “CAL”) and I think it is very good platform for building composite UI by either using WPF or Silverlight.
One of its greatest advantages is that it was done in open source manner which resulted with most of the community feedback being incorporated into lightweight, testing friendly framework. Reference implementation and samples are also good but showing only static stubs based testing which is ok but not as powerful as mocking with some mocking framework.
My mocking framework of choice is Rhino Mocks and I am going to make couple of simple blog posts showing how to test Prism code using the Rhino Mocks in couple of typical every day scenarios.
And that leads us to today’s blog post…
How to test Prism (CAL) EventAggregator based code using Rhino Mocks?
SUT I’ll be using today will be as simple as possible to deliver the message.
LoggingService is service which is responsible for handling the logging of system errors in a way that:
- subscribes to system wide events which are requested to be logged without referencing the event sources
- decides if event needs to be published (based on severity)
- if it does, it formats the system event adding the time stamp etc
- calls the publishing service which is handling the publishing of formatted event
Considering the fact that in my sample we would be having various publishing services in system publishing to different targets(eventlog, flat text file) the LoggingService gets dependency injected only a component implementing the IPublishingService.
Subscription to system wide events on a decoupled manner is possible through EventAggregator design pattern which implementation is in Prism provided through IEventAggregator service.
The LogErrorEvent is an event to which LoggerService would subscribe and which carries the event argument of EventData type containing the ErrorLevel and ErrorMessage data.
Show me the code
Enough of my blabbering (and my English), code will speak for itself much better :)
Sample used in today's blog post can be downloaded here.
LoggingService
using System;
using System.Text;
using Microsoft.Practices.Composite.Events;
namespace Example
{
public class LoggingService
{
private readonly IEventAggregator eventAggregator;
private readonly IPublishingService publishingService;
public LoggingService(IEventAggregator eventAggregator, IPublishingService publishingService)
{
this.eventAggregator = eventAggregator;
this.publishingService = publishingService;
this.eventAggregator
.GetEvent<LogErrorEvent>()
.Subscribe(this.LogIt);
}
private void LogIt(EventData eventData)
{
if (eventData.ErrorLevel<=100)
return;
var stringBuilder = new StringBuilder();
stringBuilder
.AppendFormat("Date:{0}", DateTime.Now)
.AppendLine()
.Append(eventData.ErrorMessage)
.AppendLine();
this.publishingService.PublishError(stringBuilder.ToString());
}
}
}
Nothing fancy there:
- constructor accepts two parameters which provide to LoggingService access to event aggregator and publishing service through inversion of controls.
- in constructor injected event aggregator is used for subscribing of LogIt method to LogErrorEvent.
- LogIt method is private (wouldn’t work in case of Silverlight – but that is sepa rate blog post) and does next things:
- makes sure that only events with level greater then 100 get published
- formats the given error message into appropriate format
- pass the formatted message to publishing service
IPublishingService, LogErrorEvent and EventData
namespace Example
{
public interface IPublishingService
{
void PublishError(string errorMessage);
}
}
using Microsoft.Practices.Composite.Presentation.Events;
namespace Example
{
public class LogErrorEvent : CompositePresentationEvent
{
}
}
namespace Example
{
public class EventData
{
public int ErrorLevel { get; set; }
public string ErrorMessage { get; set; }
}
}
No need to waste time commenting this…
Testing the code
I could have done test first etc, but I believe that it would obfuscate the point of this blog , which now once we see the code being tested is going to be just showing the tests
Test 1 – How to make sure that event is getting subscribed
///
/// Shows how to verify that event aggregator subscription occurred.
///
[TestMethod()]
public void Ctor_Default_WouldSubscribeToLogErrorEvent()
{
// arrange
var publishingServiceStub = MockRepository.GenerateStub<IPublishingService>();
var eventAggregatorMock = MockRepository.GenerateStub<IEventAggregator>();
var logErrorEvent = MockRepository.GenerateMock<LogErrorEvent>();
// event aggregator get event would return mocked log error event
eventAggregatorMock.Stub(p => p.GetEvent<LogErrorEvent>()).Return(logErrorEvent);
// expect that LogErrorEvent would be subscribed in constructor
logErrorEvent
.Expect(p => p.Subscribe(null))
.Return(null)
.IgnoreArguments() // we don't care which exact method or action subscribed, just that there was some.
.Repeat.Once();
// act
var loggingService = new LoggingService(eventAggregatorMock, publishingServiceStub);
// assert
logErrorEvent.VerifyAllExpectations();
}
The test is using Rhino Mocks AAA syntax introduced in 3.5 version (if you don’t know it, read Rhino Mocks Documentation Wiki excellent documentation).
In Arange section test:
- defines two stubs for the services being used (stubs because I don’t care to set any expectation related to them in this test)
- defines the mock of the event which subscription I am about to check
- stubs the event aggregator behavior so on GetEvent<LoggErrorEvent>() method call would return event mock I created.
- defines expectation on that event mock that subscription would occur once (IgnoreArguments() is there because this test doesn’t care really which method exactly would subscribe to event. Test cares only that subscription had occurred)
In Act section test just constructs the service
In Assert section test triggers verifying of the event log expectations (which in this test were: someone subscribed to this event)
(Note that making test for verifying that the Publish have occurred during the test would be pretty much the same as this test with a change on mocked expectations only)
Test 2a – How to invoke event aggregator in Act test section
Sometimes there is a behavior we want to unit test which is occurring upon the event being published through IEventAggregator and because we can be using anonymous delegate, private method handling the event (case of this blog post) there is no easy way to invoke functionality which is wanted to be tested.
This test shows how to invoke event aggregator to publish the desired event which would trigger code being tested,
In case of this example we want to test that not severe errors (error level <= 100) are not getting published.
///
/// An example of how to trigger event aggregator in act section
///
[TestMethod()]
public void LogIt_ErrorLevel100_WouldNotBePublished()
{
// arrange
var logErrorEvent = new LogErrorEvent();
var publishingServiceMock = MockRepository.GenerateMock<IPublishingService>();
var eventAggregatorStub = MockRepository.GenerateStub<IEventAggregator>();
eventAggregatorStub.Stub(p => p.GetEvent<LogErrorEvent>()).Return(logErrorEvent);
// expect that publishing service would never be called
publishingServiceMock
.Expect(p => p.PublishError(Arg<string>.Is.Anything))
.Repeat.Never();
// act
var loggingService = new LoggingService(eventAggregatorStub, publishingServiceMock);
// invoke the event aggregator
logErrorEvent.Publish(new EventData()
{
ErrorLevel = 100,
ErrorMessage = "Some error message"
});
// assert
publishingServiceMock.VerifyAllExpectations();
}
In this test, test is having in Arrange section:
- An instance of the log error event (not the mock of that event like in the case of previous test example)
- Mock of the publishing service (in previous test we had stub) because that is service we need to check it won’t be called.
- Stub of the event aggregator with stubbed GetEvent<LogErrorEvent> method
- An expectation that PublishError method of the publishing service would never be called (regardless of the parameter being sent to that method)
In Act section test is :
- constructing the logging service injecting the event aggregator stub and mock of publishing service
- invoking the Publish method on a log error event instance passing the test data (error level == 100 in this test)
In Assert section, we are just triggering checking of expectation defined on mock of the publishing service (no call made to publish error method)
Test 2b – How to invoke event aggregator in Act test section
Although from perspective of this blog post it doesn’t have a lot of value here’s the test testing that publishing service will be called in case event will be published with error level greater then 100. (The more Rhino Mocks examples we have on web, the better adopting rate will be :))
///
/// An example of how to trigger event aggregator in act section
///
[TestMethod()]
public void LogIt_ErrorLevelGreaterThen100_WouldBePublished()
{
// arrange
var logErrorEvent = new LogErrorEvent();
var publishingServiceMock = MockRepository.GenerateMock<IPublishingService>();
var eventAggregatorStub = MockRepository.GenerateStub<IEventAggregator>();
eventAggregatorStub.Stub(p => p.GetEvent<LogErrorEvent>()).Return(logErrorEvent);
publishingServiceMock
.Expect(p => p.PublishError(Arg<string>.Matches(param => param.Contains("Some error message"))))
.Repeat.Once();
// act
var loggingService = new LoggingService(eventAggregatorStub, publishingServiceMock);
logErrorEvent.Publish(new EventData()
{
ErrorLevel = 101,
ErrorMessage = "Some error message"
});
// assert
publishingServiceMock.VerifyAllExpectations();
}
Almost the same sample like previous one just with two small differences:
- In arrange section, expectation is that the PublishError method would be called once with a method parameter containing “Some error message” string (inline constrains)
- In act section, event is published with error level >100 to trigger the positive case when publishing service is been triggered
Green is nice :)

Conclusion
Thanks to P&P team and community feedback, CAL\PRISM event aggregator is implemented in such a way that mocking it is very easy (if not trivial) and every framework enabling easy testing is a good framework for me :) Good work P&P!