In this lesson, you will learn how to verify that your classes correctly wire up and tear down event handlers to prevent memory leaks and unexpected behavior.
The Problem with Event Subscriptions
When a long-lived object (like a DataService) exposes an event, dependent classes often subscribe to it. If the subscriber doesn't unsubscribe when it is disposed or no longer needed, the publisher maintains a reference to the subscriber. This is a classic source of memory leaks. Furthermore, failing to subscribe correctly means your class simply won't react to state changes in the system.
Testing the Subscription
To test this, we need a way to verify that the += and -= operators were actually triggered on the event. Since we cannot directly inspect a delegate's invocation list easily, we use the Wrapper Pattern (Lesson 24) or a Mock interface to abstract the event source.
Consider a StockMonitor that subscribes to IPriceProvider.PriceChanged.
public interface IPriceProvider {
event EventHandler<decimal> PriceChanged;
}
[Fact]
public void Monitor_ShouldSubscribeToEvent_OnInitialization() {
var mockProvider = new Mock<IPriceProvider>();
var monitor = new StockMonitor(mockProvider.Object);
// Verify the subscription happened
mockProvider.Raise(m => m.PriceChanged += null, 100m);
Assert.Equal(100m, monitor.LastPrice);
}
Testing the Unsubscription
Testing unsubscription is slightly trickier. You must ensure that after calling Dispose() or a cleanup method, the event handler is removed. We verify this by triggering the event after the cleanup and ensuring the subscriber no longer reacts.
[Fact]
public void Monitor_ShouldUnsubscribe_WhenDisposed() {
var mockProvider = new Mock<IPriceProvider>();
var monitor = new StockMonitor(mockProvider.Object);
monitor.Dispose();
// Attempt to trigger the event
mockProvider.Raise(m => m.PriceChanged += null, 200m);
// Verify the monitor did not react to the second event
Assert.NotEqual(200m, monitor.LastPrice);
}
By using mock.Raise, we simulate the event publisher’s behavior, allowing us to confirm that our class is "listening" when it should be and has "disconnected" when its lifecycle ends.
Key Takeaway: Use mocks to raise events and verify that your class updates state only when subscribed, and remains idle after explicit unsubscription.