Testing Generic Events
In the testing chapter, we introduced the XUnit assertion for testing events, Assert.Raises<T>
. Let’s imagine a doorbell class that raises a Ring
event when it is pressed, with information about the door, which can be used to do things like ring a physical bell, or send a text notification:
/// <summary>
/// A class representing details of a ring event
/// </summary>
public class RingEventArgs : EventArgs
{
private string _door;
/// <summary>
/// The identity of the door for which the doorbell was activated
public string Door => _door;
/// <summary>
/// Constructs a new RingEventArgs
/// </summary>
public RingEventArgs(string door)
{
_door = door;
}
}
/// <summary>
/// A class representing a doorbell
/// </summary>
public class Doorbell
{
/// <summary>
/// An event triggered when the doorbell rings
/// </summary>
public event EventHandler<RingEventArgs> Ring;
/// <summary>
/// The name of the door where this doorbell is mounted
/// </summary>
public string Identifier {get; set;}
/// <summary>
/// Handles the push of a doorbell
/// by triggering a Ring event
/// </summary>
public void Push()
{
Ring?.Invoke(this, new RingEventArgs(Identifier));
}
}
To test this doorbell, we’d want to make sure that the Ring
event is invoked when the Push()
method is called. The Assert.Raises<T>
does exactly this:
[Fact]
public void PressingDoorbellShouldRaiseRingEvent
Doorbell db = new Doorbell();
Assert.Raises<RingEventArgs>(
handler => db.Ring += handler,
handler => db.Ring -= handler,
() => {
db.Push();
});
This code may be a bit confusing at first, so let’s step through it. The <T>
is the type of event arguments we expect to receive, in this case, RingEventArgs
. The first argument is a lambda expression that attaches an event handler handler
(provided by the Assert.Raises
method) to our object to test, db
. The second argument is a lambda expression that removes the event handler handler
. The third is an action (also written as a lambda expression) that should trigger the event if the code we are testing is correct.
This approach allows us to test events declared with the generic EventHandler<T>
, which is one of the reasons we prefer it. It will not work with custom event handlers though; for those we’ll need a different approach.