Using Moq for unit testing with Prism EventAggregator

by: | Apr 8, 2020

[Editor’s note: This article assumes basic knowledge of unit testing, Moq, and Prism EventAggregator]

Welcome to the third and final post of our series about using Prism EventAggregator in .NET app development. In the previous two articles, I covered the Publisher-Subscriber pattern and benefits of using EventAggregator, and how to use it on your projects. Now that we’ve learned how to use EventAggregator in our app code, let’s write some unit tests. Unit testing is a vital part of releasing quality software, so we must test how we’ve used EventAggregator. However, it does present a few challenges. The two most important scenarios to test are:

  • Publisher: Verify whether an event was successfully published with the correct parameter (if applicable).
  • Subscriber: Verify whether a received event was correctly handled.

We could use the actual implementation of Prism EventAggregator. EventAggregator has very few dependencies itself, so this would probably work. But there’s a reason we shouldn’t. For a unit test, we want to isolate the code we’re testing from any kind of dependency. If we use the actual EventAggregator, our test will start to look more like an integration test rather than a unit test. So we want to use a mocked IEventAggregator instead. We’ll be using the Moq library for this.

How to test with Moq

Let’s first set up our EventAggregator example. We start with an event, a publisher, and a subscriber.

Our event has a simple string as a parameter:

public class MyTextEvent : PubSubEvent<string>
{
}

We publish the event when the user clicks a button:

public class MyPublisherViewModel
{
  private readonly IEventAggregator _eventAggregator;

  public MyPublisherViewModel(IEventAggregator eventAggregator)
  {
    _eventAggregator = eventAggregator;

    OnButtonClickCommand = new DelegateCommand(PublishTextEvent);
  }

  // ...

  private void PublishTextEvent()
  {
    _eventAggregator
      .GetEvent<MyTextEvent>()
      .Publish("text to send");
  }
}

The subscriber assigns the received text to a public property:

public class MySubscriberViewModel
{
  private readonly IEventAggregator _eventAggregator;

  public MySubscriberViewModel(IEventAggregator eventAggregator)
  {
    _eventAggregator = eventAggregator;

    _eventAggregator
      .GetEvent<MyTextEvent>()
      .Subscribe(HandleMyTextEvent);
  }

  public string Text { get; set; }

  private void HandleMyTextEvent(string text)
  {
    Text = text;
  }
}

Testing the publisher

Let’s start with the easiest problem: verifying that the event is published with the correct text when the user clicks the button. In other words, our test will have the following structure:

  • Arrange: Set up the required mocks and instantiate the PublisherViewModel.
  • Act: Execute the OnButtonClickedCommand.
  • Assert: Verify that Publish(“text to send”) was called once.

First, we need to inject a mock of IEventAggregator into our ViewModel’s constructor:

var eventAggregatorMock = new Mock<IEventAggregator>();
var viewModel = new MyPublisherViewModel(eventAggregatorMock.object);

The only method to set up is GetEvent<MyTextEvent>(). This should return an instance of MyTextEvent. We need to verify something on it, so the event also has to be a mock:

var mockedEvent = new Mock<MyTextEvent>();
eventAggregatorMock
  .Setup(x => x.GetEvent<MyTextEvent>())
  .Returns(mockedEvent.Object);

Finally, let’s verify if Publish() is called with the correct argument. Our completed test now looks like this:

// Arrange
var mockedEvent = new Mock<MyTextEvent>();
var eventAggregatorMock = new Mock<IEventAggregator>();
eventAggregatorMock
  .Setup(x => x.GetEvent<MyTextEvent>())
  .Returns(mockedEvent.Object);

var viewModel = new MyPublisherViewModel(eventAggregatorMock.object);

// Act
viewModel.OnButtonClickCommand.Execute()

// Assert
mockedEvent.Verify(x => x.Publish("text to send"), Times.Once);

The first unit test is done. Now on to the next one.

Testing the subscriber

Testing the subscriber part is a bit trickier. Let’s start with a similar structure:

  • Arrange: Set up the required mocks and instantiate the SubscriberViewModel
  • Act: Execute the code that handles the event
  • Assert: Verify that the Text property has the expected value

The problem here is in the Act phase. The event handling code is HandleMyTextEvent(string text). This method is private, so we cannot call it directly. We don’t want to make the method public because we shouldn’t change access modifiers to accommodate testing. We have to find a different approach. Somehow, we need a reference to that private method so we can invoke it manually. Luckily Moq testing provides us with a very useful method that allows us to do just that: Callback().

We start off the same way as the previous test by creating mocks of Callback()IEventAggregator and Callback()MyTextEvent and setting up the GetEvent<MyTextEvent>() method. Next, we set up MyTextEvent.Subscribe(Action eventHandler). Instead of calling Returns<T>(T value), we call Callback<T>(Action<T> callbackAction).

This Callback method receives an Action that is called whenever the method being set up is called. If the method being set up receives any arguments, those will be passed into the Action as well. In our case, Subscribe() will receive an Action<Action> argument, so our callback action will be Action. In other words, our callbackAction will receive a reference to the Action that’s passed into Publish() in the constructor of SubscriberViewModel. All we need to do is save that reference so we can invoke it later. This all sounds a bit complicated, so here it is in code:

Action<string> eventHandlerAction;
var mockedEvent = new Mock<MyTextEvent>();

mockedEvent
  .Setup(x => x.Subscribe(It.IsAny<Action<string>>()))
  .Callback(action => eventHandlerAction = action); // We store the action passed here in a variable, so we can access it later in our test.

Unfortunately, this code will throw an exception. Moq can’t setup Subscribe(Action<string>action) because that’s actually an extension method. Instead, we need to set up the method with all of its optional parameters:

mockedEvent
  .Setup(x => x.Subscribe(
    It.IsAny<Action<string>>(),      // The event handler
    It.IsAny<ThreadOption>(),        // threadOption
    It.IsAny<bool>(),                // keepSubscriberReferenceAlive
    It.IsAny<Predicate<string>>()))  // filter
.Callback((action, _, __, ___) => eventHandlerAction = action); // We're only interested in the action argument, so we discard the others

Now that we have a reference to the event handler action, we can complete our Act phase. So our complete test will look like this:

// Arrange
Action<string> eventHandlerAction;

var mockedEvent = new Mock<MyTextEvent>();
mockedEvent
  .Setup(x => x.Subscribe(
    It.IsAny<Action<string>>(),     // The event handler
    It.IsAny<ThreadOption>(),       // threadOption
    It.IsAny<bool>(),               // keepSubscriberReferenceAlive
    It.IsAny<Predicate<string>>())) // filter
.  Callback((action, _, __, ___) => eventHandlerAction = action);

var eventAggregatorMock = new Mock<IEventAggregator>();
eventAggregatorMock
.Setup(x => x.GetEvent<MyTextEvent>())
.Returns(mockedEvent.Object);

var viewModel = new MyPublisherViewModel(eventAggregatorMock.object);

// Act
eventHandlerAction.Invoke("expected");

// Assert
myTextEvent.Equal("expected", viewModel.Text);

Wow, we finally have a working unit test. But that’s a LOT of code. There should be a more elegant way to do this, right? Enter EventAggregator_Mocker.

Nuget package EventAggregator_Mocker

With the magic of refactoring and the miracle of extension methods I made this neat little nuget package. To use it, just add the package to your test project and you’re good to go.

The package contains two extension methods for Mock<IEventAggregator>:

    • Mock<TEvent> RegisterNewMockedEvent<TEvent>(Action<Action>onSubscribeAction = null) for mocking events without a parameter
    • Mock<TEvent> RegisterNewMockedEvent<TEvent, TParam>(Action<Action<TParam>> onSubscribeAction = null) for mocking events with a parameter

Both of these will return a mocked event that’s already registered within the mocked IEventAggragator. Also, they each have an optional Action parameter used as callback action, so we can get the reference to our event handling action.

With this package, our unit tests from above will now look like this:

Publisher:

// Arrange
var eventAggregatorMock = new Mock<IEventAggregator>();
Mock<MyTextEvent> mockedEvent = eventAggregatorMock.RegisterNewMockedEvent(); // We don't need a reference to the event handler, so we don't pass an Action.
var viewModel = new MyPublisherViewModel(eventAggregatorMock.object);

// Act
viewModel.OnButtonClickCommand.Execute();

// Assert
mockedEvent.Verify(x => x.Publish("text to send"), Times.Once);

Subscriber:

// Arrange
Action<string> eventHandlerAction;
var eventAggregatorMock = new Mock<IEventAggregator>();
_ = eventAggregatorMock.RegisterNewMockedEvent<MyTextEvent>(action => eventHandlerAction = action); // We don't need the event, so we discard the return value.
var viewModel = new MyPublisherViewModel(eventAggregatorMock.object);

// Act
eventHandlerAction.Invoke("expected");

// Assert
myTextEvent.Equal("expected", viewModel.Text);

Well, those code blocks are much shorter and more readable. You can find the source code of the package here. If you want to see it in action, there’s a working example. I hope you found this series on Prism EventAggregator useful. Make sure to follow us on Facebook, Twitter, and LinkedIn and let us know what you think of it.


Need help with your .NET app development project?

ArcTouch has been designing and developing apps for leading companies since the dawn of the app store. Contact us if you need advice or help with your latest app projects. From cross-platform Xamarin app development to native Android and iOS projects, we’re here to help.


Subscribe to our newsletter

By providing your information, you are agreeing to ArcTouch's privacy policy.