Yet another simplification of Prism’s EventAggregator syntax part II–mocking extension methods


After my last post, I had quite a healthy discussion with Alex Hung (@alexhung) on you guessed it, testability and mocking. Alex’s observation was although my extension methods were easy to use, they were difficult to test. He had gone and implemented a similar user experience but through using a custom wrapper which added Publish and Subscribe methods but called to EA under the covers. His wrapper implements an interface that is easy to mock.

The question then was how can you mock the new extension methods? Before getting into the how lets first answer the question of why?

Here are unit tests that should illustrate what I want to do.

[TestFixture]
public class When_SelectOrder_is_invoked {
  [Test]
  public void then_controller_publishes_OrderSelected() 
  {
    //mock out the event aggregator;  
    var mock = new Mock<IEventAggregator>() 
    var controller = new OrderController(mock.Object);
    var order=new Order();
    controller.SelectOrder(order);
    mock.Verify(ea=>ea.Publish(order));
  }
}

[TestFixture]
public class When_OrderViewModel_is_created {
  [Test]
  public void then_subscribes_to_OrderSelected() {
    //mock out the event aggregator;  
    var mock = new Mock<IEventAggregator>() 
    var vm = new OrderViewModel(mock.Object);
    mock.Verify(ea=>ea.Subscribe(vm.OnOrderSelected));
  }
}

Looks simple enough right? I am creating a mock EventAggregator, passing it in and then verifying that the Publish and Subscribe methods are called. Both Alex and I originally though that would work. Winking smile Though Alex was a bit skeptical.

However the reality is that it cannot work. The reason is because I am creating a mock of IEventAggregator but the methods I want to mock are not on the event aggregator itself, they are extension methods! Yet another proof that what looks like it will work in the blackboard of your mind, often doesn’t hold up any water in the real world.

There is hope though. Extension methods CAN be mocked, but it takes a bit of refactoring. Daniel Cazzulino (@kzu) has a great post on this here. The approach I came up with is based on his. For my extension methods here are the steps I went through in that refactoring.

  1. Created an IEventAggregatorExtensionsProvider interface. This interface has all the methods from my EventAggregatorExtensions class however, the methods are all non-static (obviously) and I removed this from the first param. Other than that the signatures are the same.
  2. Created EventAggregatorExtensionsProvider which implements IEventAggregatorExtensionsProvider. I ripped the implementation of each method out of EventAggregatorExtenions and then moved it into this provider.
  3. Added a private static variable _Provider of type IEventAggregatorExtensionsProvider to EventAggregatorExtensions. The variable initializes itself to a new instance of EventAggregatorExtensionsProvider.
  4. Changed all public methods in EventAggregatorExtensions to delegate directly to the _provider instance.
  5. Added a static SetProvider method which accepts an IEventAggregatorExtensionsProvider which it then overrides _provider with.

Here’s what the resulting code now looks like (which has grown a bit from my first Codepaste): http://codepaste.net/woqq1d

With these changes I can now pass in a mock of IEventAggregatorExtensionsProvider to do exactly what I want. Now my unit tests read like this:

[TestFixture]
public class When_SelectOrder_is_invoked {
  [Test]
  public void then_controller_publishes_OrderSelected()
  {
    //mock out the provider
    var mock = new Mock<IEventAggregatorExtensionsProvider>();
    var ea = new EventAggregator();
    EventAggregatorExtensions.SetProvider(mock.Object);
    var controller = new OrderController(ea);
    var order = new Order();
    controller.SelectOrder(order);
    mock.Verify(p=>p.Publish(ea, order));
}

[TestFixture]
public class When_OrderViewModel_is_created {
  [Test]
  public void then_subscribes_to_OrderSelected()
  {
    //mock out the provider
    var mock = new Mock<IEventAggregatorExtensionsProvider>();
    var ea = new EventAggregator();
    EventAggregatorExtensions.SetProvider(mock.Object);
    var vm = new OrderViewModel(ea);
    mock.Verify(p=>p.Subscribe(ea, vm.OnOrderSelected));
    
  }
}

The main difference is that I am now creating a mock of the extensions provider rather than the event aggregator itself. That means that my mock is the thing that will get invoked by the classes under test.

Actually the EA instance I create in the unit tests is just a dummy and could even be null as extensions methods can be invoked on null instances. That’s because my classes under test are only accessing the extension methods. However I passed it in because it is entirely conceivable one might want to access the actual instance methods.

To be fair there is more ceremony here than the approach that Alex was suggesting as you have to mock the provider and pass in an event aggregator. However the difference I think is marginal, and it works nicely and is completely testable.

What do you think?

Comments (3)

  1. Omer Mor says:

    There's been an alternative approach to the EA pattern using Microsoft's Reactive Extensions (Rx) observables:

    jfromaniello.blogspot.com/…/event-aggregator-with-reactive.html

    Last I heard, Caliburn was adopting this approach in its trunk.

    It is a very elegant and simple solution.

  2. Julian Dominguez says:

    Hi Glenn. Why not avoid the problem entirely, and just import the Event class itself instead of the EventAggregator…? You just need to make sure that the import is a singleton, and that's it, with MEF is extremely simple.

    You could also do this with Unity by writing an extension to make sure it returns a singleton instead of newing up the class, as it is the default behavior when specifying concrete types without registering them.

    This is not just easier to test and read, but you also define the real dependency (which is the event itself, not the EventAggregator) for the consumer class in its constructor.

  3. gblock says:

    Hey Julian

    Yeah I did a blog post with that exact recommendation here: blogs.msdn.com/…/event-aggregation-with-mef-with-and-without-eventaggregator.aspx so I do like that approach.

    Having the event class be a dependency is much more intention revealing in the code as it is in the signature of the apis rather than inline. However I was trying to work within the existing model.

    What you are saying would work though for sure.

    Glenn