Testing callbacks with Rhino.Mocks

I’m currently writing tests for a piece of code that has lot of events flying here and back. Something like:

public class Caller

{

    public event EventHandler<CancelEventArgs> MyEvent;

 

    public void Call()

    {

        if (MyEvent != null)

            MyEvent(null, new CancelEventArgs(false));

    }

}

If I not only want to test if the event was fired but also examine it’s arguments I end up writing quite a lot of code to do simple thing.

First I have to define IEventSubscriber interface (contrary to IEventRaiser).

public interface IEventSubscriber<TEventArgs> where TEventArgs : EventArgs

{

    void Method(object sender, TEventArgs e);

}

Next I have to initialize it in my SetUp method

 

[SetUp]

public void SetUpTests()

{

    _mocks = new MockRepository();

    _subscriber = _mocks.CreateMock<IEventSubscriber<CancelEventArgs>>();

}

This is however quite dumb plumbing code, and I have to write it once (or once per TestFixture, in case of initialization). The worst part are the tests alone:

[Test]

public void When_Call_called_Caller_should_raise_MyEvent_with_cancel_set_to_false()

{

    using (_mocks.Record())

    {

        Expect.Call(delegate { _subscriber.Method(null, null); }).IgnoreArguments().Do(

            new EventHandler<CancelEventArgs>(

                delegate(object o, CancelEventArgs e) { Assert.IsFalse(e.Cancel); }));

    }

   using (_mocks.Playback())

   {

       Caller caller = new Caller();

       caller.MyEvent += _subscriber.Method;

       caller.Call();

   }

}

My concern is that lines 6-8 are just one big blob of code, and you have to read it carefully, to find out what it actually does. Another thing is inability to use anonymous delegates where type Delegate is expected, which results in more verbose, less readable code.

After writing few tests like this I decided to do something with this ugly piece of code, and thanks to the magic of generics I came up with a helper method:

private void ExpectCallback<TEventArgs>(IEventSubscriber<TEventArgs> subscriber, EventHandler<TEventArgs> handler)

    where TEventArgs : EventArgs

{

    Expect.Call(delegate { subscriber.Method(null, null); }).IgnoreArguments().Do(handler);

}

With it I can shorten my tests to look much nicer:

[Test]

public void Test_Callback_in_C_Sharp_20()

{

    using(_mocks.Record())

    {

        ExpectCallback(_subscriber,

                       delegate(object o, CancelEventArgs e) { Assert.IsFalse(e.Cancel); });

    }

    using(_mocks.Playback())

   {

       Caller caller = new Caller();

       caller.MyEvent += _subscriber.Method;

       caller.Call();

   }

}

and with lambdas, even to:

[Test]

public void Test_Callback_in_C_Sharp_30()

{

    using (_mocks.Record())

    {

        ExpectCallback(_subscriber, (o, e) => Assert.IsFalse(e.Cancel));

    }

    using (_mocks.Playback())

    {

       Caller caller = new Caller();

       caller.MyEvent += _subscriber.Method;

       caller.Call();

   }

}

It not only improves readability of my code, but thanks to generic constraints I get more compile-time type checking. Now I can get back to testing:)

Technorati Tags: , ,
  • Jeff

    Awesome, thanks for the post….just saved me from beating my head against the wall.