Mocking Events

Currently in Rhino.Mocks, making mocks fire events and ensuring that an event on your SUT was fired are both awkward and verbose at best. Here is an example of both things at once:

[Test]
public void ViewFiresBeginDrag_Always_FiresChangedEvent()
{
  IEventRaiser raiser;
  bool eventFired = false;
  using (Record)
  {
    The<ICardView>().BeginDrag += null;
    raiser = LastCall.IgnoreArguments().GetEventRaiser();

    Target.Changed += (x,y) => eventFired = true;
  }

  using (Playback)
  {
    raiser.Raise(The<ICardView>(), EventArgs.Empty);
    Assert.IsTrue(eventFired);
  }
}

Nice eh? First, is there a better way to do either of these things that I’m missing? Please tell me if so. Next, if not, what can we do to clean this up?

Well, Ayende and I discussed this in the past and Ayende spiked it and asked for feedback. The feedback was mixed and for one reason or another it never made it into Rhino.Mocks that I know of.

Well, today while I was working on Machine.Testing and another side project, I decided to give something else a shot. What I ended up with is this:

[Test]
public void ViewFiresBeginDrag_Always_FiresChangedEvent()
{
  using (Record)
  {
    PrimeEventFiringOn<ICardView>(x => x.BeginDrag += null);
    Target.Changed += NewEventFireExpectation<EventHandler>();
  }

  using (Playback)
  {
    FireEventOn<ICardView>(EventArgs.Empty);
  }
}

Better, but I still don’t think it’s perfect. Also, it probably requires some explanation, so let’s pick it apart piece by piece:

    PrimeEventFiringOn<ICardView>(x => x.BeginDrag += null);

=> +=??? Ugly huh? I really wish we could just refer to an event somehow without having to do +=/-=. At least I’m not using vb though… Alas, we cannot access them easily so we’re stuck hacking away like this. So this particular method will basically get the mock or stub ICardView.BeginDrag ready to be fired. This needs to be done during the record phase it seems. You can only prime one event at a time per mock, so if you need to do more than one you can revert to the normal Rhino.Mocks syntax.

    Target.Changed += NewEventFireExpectation<EventHandler>();

This was a fun method. This method actually creates a new DynamicMethod in the signature required by the event and creates a new delegate. The method tracks whether or not it was fired, and at the end of the Playback phase in my fixture it will assert that all of the events were actually fired.

    FireEventOn<ICardView>(EventArgs.Empty);

This actually fires the event we set up in the Prime call. It can only be called after you’ve primed an event.

So, the whole thing is kind of "magic", but it’s less code if you can accept the magic. I think we can make things even better though, but it’d require changes to Rhino.Mocks and it’s time for bed so maybe Ayende can swing by the Eleutian office while in Seattle and we can work on it. Here’s what I’m thinking:

[Test]
public void ViewFiresBeginDrag_Always_FiresChangedEvent()
{
  using (Record)
  {
    Target.Changed += Mocks.CreateEventHandler<EventHandler>();
  }

  using (Playback)
  {
    EventRaiser.Raise(() => The().BeginDrag += null, The<ICardView>(), EventArgs.Empty);
  }
}

There’s probably more you can do with the CreateEventHandler syntax like add more specific expectations, assert its not fired, assert that it’s fired X times, etc. The EventRaiser syntax is ugly, but it doesn’t involve strings or the fire from the right hand side like the syntax I mentioned at the beginning of the post.

You can get the source here, but I warn you it’s first draft and there are hacky bits.

Oh, and here’s some example tests using the TestsFor fixture:

[TestFixture]
public class CardPresenterTests : TestsFor<CardPresenter>
{
  private Card _card;

  public override void SetupContainer()
  {
    Override<ICardView>(With.Stub);
  }

  public override void BeforeEachTest()
  {
    _card = new Card(0);
  }

  [Test]
  public void OnBeginDrag_Always_SetsIsInFluxToTrue()
  {
    using (Record)
    {
      PrimeEventFiringOn<ICardView>(x => x.BeginDrag += null);
    }

    using (Playback)
    {
      FireEventOn(EventArgs.Empty);

      Assert.That(The<ICardView>().IsInFlux, Is.True);
    }
  }

  [Test]
  public void OnBeginDrag_Always_FiresChangedEvent()
  {
    using (Record)
    {
      PrimeEventFiringOn<ICardView>(x => x.BeginDrag += null);
      Target.Changed += NewEventFireExpectation<EventHandler>();
    }

    using (Playback)
    {
      FireEventOn<ICardView>(EventArgs.Empty);
    }
  }
}
This entry was posted in frameworks, mocking. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://www.exotribe.com/appliedmagic Tim Binkley-Jones

    I don’t know about providing events on mocked objects, but I use a pretty simple class to ensure that some code under test does indeed call an event. I’ve written about it here: http://www.exotribe.com/node/36