Search code examples
unit-testingeventstestingtdd

Testing Events from Objects


I have been trying to get more in to TDD. Currently keeping it simple with lots of Debug.Asserts in a console application.

Part of the tests I wanted to complete was ensuring that events were raised from the object, the correct number of times, since client code will depend on these events.

So, I began thinking about how to test the events were being raised, and how I could track them. So I came up with a Monitor "pattern" (if you could call it that). This is basically a class that takes an object of the type under test in its constructor.

The events are then hooked up to the monitor, with delegates created which both count and log when the event is raised.

I then go back to my test code and do something like:

    bool Test()
    {
        MyObject mo = new MyObject();
        MyMonitor mon = new MyMonitor(mo);

        // Do some tests that should cause the object to raise events..

        return mon.EventCount == expectedCount;
    }

This works fine, and when I intentionally busted my code, the tests failed as expected, but I wonder, is this too much "free form" test code (i.e. code without supporting tests)?


Additional Thoughts

  • Do you test for events?
  • How do you test for events?
  • Do you think the above has any holes/room for improvement?

All input gratefully received! ^_^


Solution

  • The easiest thing you can do is subscribe an anonymous method or a lambda to the event and increment a counter that's local to your test in it. No need to use an extra class at all.

    I found this won't make your code very readable so I've done about the same thing. I've written monitor objects in several projects. Usually they're a bit more generic than your monitor. They just expose public methods that you can subscribe to events and they count the number of times they've been called. This way you can reuse monitor objects for different events.

    Something like this:

    MyObject subjectUnderTest = new MyObject();
    EventMonitor monitor = new Monitor();
    subjectUnderTest.Event += monitor.EventCatcher;
    
    // testcode;
    
    Assert.Equal( 1, monitor.EventsFired );
    

    The problem with this is that it's not really generic. You can only test events that monitor.EventCatcher() can be subscribed to. I usually don't do events with arguments so that's no problem, I just have the standard void EventCatcher(object sender, EventArgs arguments). You could make this more generic by subscribing a lambda of the right type to the event and calling EventCatcher in the lambda. This makes your tests a bit harder to read though. You can also use generics to make the EventCatcher method work with the generic EventHandler.

    You might want to look out, eventually you'll want to be able to store exactly what events were called in what order and with what parameters. Your eventmonitor can easilly get out of hand.


    I found another way of doing this that might make sense for tests with more complex assertions.

    Instead of creating your own monitor you let your mocking framework of choice create it for you, you just create an interface for the class that handles the event. Something like this:

    public interface IEventHandlerStub
    {
        event EventHandler<T> Event(object sender, T arguments);
    }
    

    Then you can mock up this interface in your test. Rhino Mocks does this like this:

    var eventHandlerStub = MockRepository.GenerateStub<IEventHandlerStub>();
    myObject.Event += eventHandlerStub.Event;
    
    // Run your code
    
    eventHandlerStub.AssertWasCalled(x => x.Event(null, null));
    

    For a simple test like this it might be overkill but if you want to assert things about parameters for example you can use the flexibility of your mocking framework for it.

    On another note. Rob and I are working on a generic event-testing monitor class that might make some of this more easy. If people are interested in using something like this I'd like to hear from you.