Saturday, December 19, 2015

Event testing with the constraint model

In my previous post about the NUnit constraint model, I briefly touched into the new constraint model which is used to write more elegant asserts. As I mentioned, the constraint model is more than just syntactical sugar. I have had a lot of fun with extending the constraint model to allow for easy event testing, and I'd like to share it with you. This is my Christmas gift to you!

Testing events - traditional method

In order to test that an event has been fired from the tested object, one would typically do something like this:

// Arrange
var testee = new Testee();
bool eventWasFired = false;
testee.MyEvent += (s,e) => eventWasFired = true;

// Act
testee.SomeMethod();

// Assert
Assert.That(eventWasFired, Is.True);

In order to test a PropertyChangedEvent, the syntax becomes a bit more complex and harder to read:

// Arrange
var testee = new Testee();
bool eventWasFired = false;
model.PropertyChanged += (s, e) =>
                            {
                              if (e.PropertyName == "SomeProperty")
                              {
                                eventWasFired = true;
                              }
                            };

// Act
testee.SomeMethod();

// Assert
Assert.That(eventWasFired, Is.True);

This can be tweaked as needed to assert that an event was NOT fired or to assert that the event was fired a given number of times. However, it is not as obvious and readable as one would like. Keep in mind the axe murderer who is going to read your tests!

Having so much more fun with the constraint model

One of the advantages of the NUnit constraint model is the extensibility. My event asserts allows you to test for events like this:

// Arrange
var testee = new Testee();
Listen.To(testee);

// Act
testee.SomeMethod();

// Assert
Assert.That(testee, Did.Fire.TheEvent("SomeEvent"));

I dare to say that this is slightly more readable and less complex than the traditional method! Specifying the event with a string is not typesafe, which is a bit sad. You will not get a compile error if the event does not exist. However, the assert will throw an IllegalArgumentException if the event does not exist. Hence, you will not get false positives or negatives because the event was misspelled.

The difference is even more obvious when testing for a PropertyChangedEvent (compare with the second traditional example):

// Arrange
var testee = new Testee();
Listen.To(testee);

// Act
testee.SomeMethod();

// Assert
Assert.That(testee, Did.Fire.ThePropertyEvent("SomeProperty"));

The other variants can be tested with

Assert.That(testee, Did.Not.Fire.TheEvent("SomeEvent"));
Assert.That(testee, Did.Fire.TheEvent("SomeEvent").Once);
Assert.That(testee, Did.Fire.ThePropertyEvent("SomeProperty").Times(3));

I have uploaded the source code with the required constraints here. Simply add them to your test project or a shared test helper project, and you are good to go. Code samples in the form of tests (of course) are included in the DidTest class. The constraint class is doing a lot of safeguarding to prevent the developer from using a wrong syntax.

Have fun!