views:

165

answers:

7

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! ^_^

A: 

looks fine to me - since it works ;-)

Steven A. Lowe
+4  A: 

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.

Mendelt
+1  A: 

One hole/room for improvement is that your Monitor only counts the numbers of events raised. Ideally, one would be able to specify expectations of which events should be raised, how many times, in which order, and maybe even what the (object sender, EventArgs e) pair should look like when each event is raised.

siz
Good point, the case presented was rather simplistic. In some of the checks it will only increment the count if certain conditions are met etc.
Rob Cooper
A: 

I do test event. The way I do it is pretty simple.

private int counterEvent;

[Test]
public void abcTest()
{
 counterEvent = 0;
 //1- Initialize object to test
 //2- Set the event to test (abcTest_event for example)
 //Assert the object incremented (counterEvent for example)
}

private void abcTest_event(object sender)
{
 counterEvent++;
}
Daok
A: 

In my test classes, I just hook in event handlers to the events of the objects declared... or am I missing something here??

kpollock
Not missing anything.. I was doing that, but I soon found that code got messy, (resetting counters etc.) - encapsulating the "monitoring" into a monitor class seemed like the right thing to do.
Rob Cooper
+1  A: 

I usually test for events by using a Mock object - as I work in Java, I use EasyMock (something similar should exist for your language of choice):

FooListener listener = createMock(FooListener.class);
expect(listener.someEvent());
replay(listener);
myObject.addListener(listener);
myObject.doSomethingThatFiresEvent();
verify(listener);

What you are doing sounds more like a Stub to me - that is, the Listener/Observer doesn't know how often it is supposed to be called, but just counts the number of calls and then the tests asserts against that count. That's a reasonable solution, too, and which one you prefer is mostly a matter of personal preference - and probably of which solution is made easier by your language and available tools.

Take a look at this article on the topic.

Ilja Preuß
+2  A: 

I recently wrote a series of blog posts on unit testing event sequences for objects that publish both synchronous and asynchronous events. The posts describe a unit testing approach and framework, and provides the full source code with tests.

I describe the implementation of an "event monitor" similar in concept to what you describe and some of the previous answerers to this question have mentioned. Definitely in the right direction, as writing lots of tests without some sort of monitor pattern results in lot of messy boilerplate code.

Using the event monitor described in my article, tests can be written like so:

AsyncEventPublisher publisher = new AsyncEventPublisher();

Action test = () =>
{
    publisher.RaiseA();
    publisher.RaiseB();
    publisher.RaiseC();
};

var expectedSequence = new[] { "EventA", "EventB", "EventC" };

EventMonitor.Assert(test, publisher, expectedSequence, TimeoutMS);

The EventMonitor does all the heavy lifting and will run the test (test) and assert that events are raised in the expected sequence (expectedSequence). It also prints out nice diagnostic messages on test failure. What is different from some of the approaches discussed is it does not just count, it will assert that the exact sequence specified has been followed. Also, you do not need to hook any events. That is all handled by the event monitor. The tests are therefore quite clean.

There's a lot of detail in the posts describing the issues and approaches, and source code too:

http://gojisoft.com/blog/2010/04/22/event-sequence-unit-testing-part-1/

chibacity
Nice :) Thanks for sharing!
Rob Cooper