Aug 4, 2010

Mocking Comparison – Part 10: Events

So far in our comparison we’ve been looking at mock objects as if they were much like any other object, but what happens when we want our mocks to either raise or subscribe to events?

If you’re testing how your class under test reacts when it receives an event, or want to know if it raises an event with correct values then you really need your mock framework to be able to support this.

Subscribing To Events

To get a mock object to subscribe to an event is pretty easy.  Just do it like you normally would, and then if you want to assert anything about how the event was raised, simply assert that the subscription method was called as expected.

Rhino Mocks

[Fact]
public void Rhino_event_subscriber()
{
var monkey = MockRepository.GenerateMock<IMonkey>();
var keeper = new ZooKeeper();

keeper.OnBananaReady += monkey.BananaReady;

keeper.FeedMonkeys();
monkey.AssertWasCalled(m => m.BananaReady(
Arg<object>.Is.Equal(keeper)
,Arg<BananaEventArgs>.Matches(b => b.IsRipe
)));
}

As you can see, we subscribe to the event normally, with the event being raised by the FeedMonkeys() call.

We then check that the event was called correctly and that the IsRipe flag was set correctly.

Moq

[Fact]
public void Moq_event_subscriber()
{
var monkey = new Mock<IMonkey>();
var keeper = new ZooKeeper();

keeper.OnBananaReady += monkey.Object.BananaReady;

keeper.FeedMonkeys();
monkey.Verify(m => m.BananaReady(keeper,
It.Is<BananaEventArgs>(b => b.IsRipe)));
}

The syntax is much the same as for Rhino Mocks apart from the fact that the .Object. syntax again makes things feel clunky.  On the positive side of things, the better constraint syntax in Moq makes the verify call far less noisy.

NSubstitute

[Fact]
public void Nsubstitute_event_subscriber()
{
var monkey = Substitute.For<IMonkey>();
var keeper = new ZooKeeper();

keeper.OnBananaReady += monkey.BananaReady;

keeper.FeedMonkeys();
monkey.Received().BananaReady(keeper,
Arg.Is<BananaEventArgs>(e => e.IsRipe));
}

The NSubstitute version is looks much the same as the Moq syntax, just without the .Object. stuff.

Raising Events

Raising an event is a pretty simply process.  We simply get our class under test to subscribe to an event on our mock object and then ask our mock to raise an event.

Rhino Mocks

[Fact]
public void Rhino_raising_an_event()
{
var monkey = MockRepository.GenerateMock<IMonkey>();
var tourist = new Tourist();

tourist.SeeAMonkey(monkey);

monkey.Raise(m => m.Dance += null, monkey, new EventArgs());
Assert.Equal(1, tourist.PhotosTaken);
}

The line to pay attention to here is the second last one.  The monkey.Raise() call.  What you notice in here is that in order to raise an event we need to pass in an expression that registers an empty event listener.  This is so Rhino can pick up the signature of the event you wish to raise.

Once that’s done we just provide arguments for the sender and event args that we wish to use for the event.

It’s a little weird looking but it works.

Moq

[Fact]
public void Moq_raising_an_event()
{
var monkey = new Mock<IMonkey>();
var tourist = new Tourist();

tourist.SeeAMonkey(monkey.Object);

monkey.Raise(m => m.Dance += null, new EventArgs());
Assert.Equal(1, tourist.PhotosTaken);
}

The Moq code is much the same as the Rhino code, with the only difference being that by default the sender is the object raising the event and we don’t need to specify it.

NSubstitute

[Fact]
public void Nsubstitute_raising_an_event()
{
var monkey = Substitute.For<IMonkey>();
var tourist = new Tourist();

tourist.SeeAMonkey(monkey);

monkey.Dance += Raise.Event(new EventArgs());
Assert.Equal(1, tourist.PhotosTaken);
}

Here you see the code is much simpler.  We still need to register a pseudo event listener when we want to raise the argument, but at the same time, it’s a little more obvious what’s going on.

All three frameworks suffer from needing to use event subscriptions to figure out what event to fire, but that’s a result of limitations in the way C# works rather than a poor design decisions.  With that in mind the verdict on which syntax to choose goes to NSubstitute.  Since event subscription is much the same across all 3 frameworks there’s not much to differentiate them, however event raising in NSubstitute is cleaner and more expressive than the syntax of both Rhino and Moq, making my choice rather easy. 

 

Other posts in this series:

2 comments:

  1. Do you know of a way in actually determining if an event has an event handler? See this StackOverflow question. http://stackoverflow.com/q/9364254/84539

    ReplyDelete