Question

I have a class, that subscribes to an event via PRISMs event aggregator.

As it is somewhat hard to mock the event aggregator as noted here, I just instantiate a real one and pass it to the system under test.

In my test I then publish the event via that aggregator and then check how my system under test reacts to it. Since the event will be raised by a FileSystemWatcher during production, I want to make use of the automatic dispatch by subscribing on the UIThread, so I can update my UI once the event is raised.

The problem is, that during the test, the event never gets noticed in the system under test unless I don't subscribe on the UIThread.

I am using MSpec for my tests, which I run from inside VS2008 via TDD.Net. Adding [RequiresSta] to my test class didn't help

Does anyone have a solution, that saves me from changing the ThreadOption during my tests (e.g. via a property - what an ugly hack)???

Was it helpful?

Solution

If you mock both the event and the Event Aggregator, and use moq's Callback, you can do it.

Here's an example:

Mock<IEventAggregator> mockEventAggregator;
Mock<MyEvent> mockEvent;

mockEventAggregator.Setup(e => e.GetEvent<MyEvent>()).Returns(mockEvent.Object);

// Get a copy of the callback so we can "Publish" the data
Action<MyEventArgs> callback = null;

mockEvent.Setup(
    p =>
    p.Subscribe(
        It.IsAny<Action<MyEventArgs>>(), 
        It.IsAny<ThreadOption>(), 
        It.IsAny<bool>(), 
        It.IsAny<Predicate<MyEventArgs>>()))
        .Callback<Action<MyEventArgs>, ThreadOption, bool, Predicate<MyEventArgs>>(
        (e, t, b, a) => callback = e);


// Do what you need to do to get it to subscribe

// Callback should now contain the callback to your event handler
// Which will allow you to invoke the callback on the test's thread
// instead of the UI thread
callback.Invoke(new MyEventArgs(someObject));

// Assert

OTHER TIPS

I really think you should use mocks for everything and not the EventAggregator. It's not hard to mock at all... I don't think the linked answer proves much of anything about the testability of the EventAggregator.

Here's your test. I don't use MSpec, but here's the test in Moq. You didn't provide any code, so I'm basing it on the linked-to code. Your scenario is a little harder than the linked scenario because the other OP just wanted to know how to verify that Subscribe was being called, but you actually want to call the method that was passed in the subscribe... something more difficult, but not very.

//Arrange!
Mock<IEventAggregator> eventAggregatorMock = new Mock<IEventAggregator>();
Mock<PlantTreeNodeSelectedEvent> eventBeingListenedTo = new Mock<PlantTreeNodeSelectedEvent>();

Action<int> theActionPassed = null;
//When the Subscribe method is called, we are taking the passed in value
//And saving it to the local variable theActionPassed so we can call it.
eventBeingListenedTo.Setup(theEvent => theEvent.Subscribe(It.IsAny<Action<int>>()))
                    .Callback<Action<int>>(action => theActionPassed = action);

eventAggregatorMock.Setup(e => e.GetEvent<PlantTreeNodeSelectedEvent>())
                   .Returns(eventBeingListenedTo.Object);

//Initialize the controller to be tested.
PlantTreeController controllerToTest = new PlantTreeController(eventAggregatorMock.Object);

//Act!
theActionPassed(3);

//Assert!
Assert.IsTrue(controllerToTest.MyValue == 3);

You may not like this as it may involve what you feel is an "ugly hack", but my preference IS to use a real EventAggregator rather than mocking everything. While ostensibly an external resource, the EventAggregator runs in memory and so does not require much set-up, clear down, and is not a bottle neck like other external resources such as databases, web-services, etcetera would be and therefore I feel it is appropriate to use in a unit test. On that basis I have used this method to overcome the UI thread issue in NUnit with minimal change or risk to my production code for the sake of the tests.

Firstly I created an extension method like so:

public static class ThreadingExtensions
{
    private static ThreadOption? _uiOverride;

    public static ThreadOption UiOverride
    {
        set { _uiOverride = value; }
    }

    public static ThreadOption MakeSafe(this ThreadOption option)
    {
        if (option == ThreadOption.UIThread && _uiOverride != null)
            return (ThreadOption) _uiOverride;

        return option;
    }

}

Then, in all my event subscriptions I use the following:

EventAggregator.GetEvent<MyEvent>().Subscribe
(
    x => // do stuff, 
    ThreadOption.UiThread.MakeSafe()
);

In production code, this just works seamlessly. For testing purposes, all I have to do is add this in my set-up with a bit of synchronisation code in my test:

[TestFixture]
public class ExampleTest
{
    [SetUp]
    public void SetUp()
    {
        ThreadingExtensions.UiOverride = ThreadOption.Background;
    }

    [Test]
    public void EventTest()
    {
        // This doesn't actually test anything useful.  For a real test
        // use something like a view model which subscribes to the event
        // and perform your assertion on it after the event is published.
        string result = null;
        object locker = new object();
        EventAggregator aggregator = new EventAggregator();

        // For this example, MyEvent inherits from CompositePresentationEvent<string>
        MyEvent myEvent = aggregator.GetEvent<MyEvent>();

        // Subscribe to the event in the test to cause the monitor to pulse,
        // releasing the wait when the event actually is raised in the background
        // thread.
        aggregator.Subscribe
        (
            x => 
            {
                result = x;
                lock(locker) { Monitor.Pulse(locker); }
            },
            ThreadOption.UIThread.MakeSafe()
        );

        // Publish the event for testing
        myEvent.Publish("Testing");

        // Cause the monitor to wait for a pulse, but time-out after
        // 1000 millisconds.
        lock(locker) { Monitor.Wait(locker, 1000); }

        // Once pulsed (or timed-out) perform your assertions in the real world
        // your assertions would be against the object your are testing is
        // subscribed.
        Assert.That(result, Is.EqualTo("Testing"));
    }
}

To make the waiting and pulsing more succinct I have also added the following extension methods to ThreadingExtensions:

    public static void Wait(this object locker, int millisecondTimeout)
    {
        lock (locker)
        {
            Monitor.Wait(locker);
        }
    }

    public static void Pulse(this object locker)
    {
        lock (locker)
        {
            Monitor.Pulse(locker);
        }
    }

Then I can do:

// <snip>
aggregator.Subscribe(x => locker.Pulse(), ThreadOption.UIThread.MakeSafe());

myEvent.Publish("Testing");

locker.Wait(1000);
// </snip>

Again, if your sensibilities mean you want to use mocks, go for it. If you'd rather use the real thing, this works.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top