It’s easy to forget (or choose not to) test events when you’re writing test-driven code, but they mustn’t be ignored because client code which subscribes to your events will depend on them firing under the correct circumstances. And without tests to prove this happens, there is no guarantee that they are doing their job properly, which will have a serious impact on all of the subscribers.
What might deter developers is the pub/sub nature of events, but this is no barrier to testing their behaviour. Assuming the event is triggered by a call to a synchronous method, then the result will be a call by the same thread (therefore on the same call stack) to one or more event handler delegates – so there are no asynchronous timing or thread issues to be worry about. Handling asynchronous events is slightly more difficult which I discuss at the bottom of this article.
Here is an example based on a retail website’s shopping basket (cart) which fires an event whenever an item is added or removed from the basket. To start with, here’s an NUnit test to check that an event fires whenever an item is added to the basket. It requires a field to tell us if the event has fired, and an event handler which sets the field to true. The event handler
basket_ItemCountChanged is then hooked up to the
The test currently doesn’t compile because the types/methods/properties marked in red don’t exist yet, so here is the code to make it pass. There is a simple product interface and concrete class which is what gets added to the basket.
And here is the shopping basket itself. It’s generic so that it can store any type, has an internal collection of items of type T, and fires the ItemCountChanged event whenever an item is added to the basket. Note: to keep the example simple, I have excluded code to make shared data access thread-safe.
Refactoring the test
The test now passes, however it’s a bit messy. So we can simplify it by dispensing with the
_eventFired field and
basket_ItemCountChanged event handler and replacing them with a local variable and an anonymous delegate in the form of a simple lambda expression. Not only does this make the test completely self-contained, but it’s a lot neater and more readable now.
Finally, re-run the test which goes green.
In the next post I discuss testing asynchronous events.