Event Sequence Unit Testing – Part 1

By , April 22, 2010 4:04 pm

A lot of the development we’ve been doing here at GojiSoft has involved event driven component development.  We didn’t like the endless boiler plate unit test code that we had to write to unit test classes that raise events, so we came up with a few helper classes that make it effortless.  A test now looks like this:

[Test] 
public void TestEventRaisingSequence() 
{ 
      var myClass = new MyEventRaisingClass(); 
 
      Action test = () => { myClass.RaiseA(); myClass.RaiseB(); }; 

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

      EventMonitor.Assert(test, myClass, expectedSequence); 
} 

The framework handles synchronous & asynchronous event raising, as well as property change notification (INotifyPropertyChanged).

Download Source CodeDocumentation

This four part article describes how the framework works and how it evolved from boilerplate unit tests to a pain-free framework that generates IL on the fly.

  • Part 1 Unit testing event sequences.
  • Part 2 Developing an event monitor to test an object’s events.
  • Part 3 Using Reflection and MSIL (Intermediate Language) to implement a generic event monitor.
  • Part 4 Reflection and MSIL code for dynamic event subscription in detail.
  • Conclusion Using the EventMonitor to write [Test]s.

Part 1

An aspect of developing components that raise events is writing unit tests to codify expected event sequences e.g. when I change state s1 and then s2, I expect event e1 then event e2 to be raised. This style of testing helps to codify event firing expectations and then to keep those expectations locked down as the system evolves - classic unit testing goals.

Given a shortened example of an event publishing type EventPublisher:

public class EventPublisher
{
    public event EventHandler EventA, EventB;

    public void RaiseA()
    {
        EventA(this, EventArgs.Empty);
    }

    public void RaiseB()
    {
        EventB(this, EventArgs.Empty);
    }
}

We can write unit tests against it as follows:

[Test]
public void EventSequenceTest()
{
    bool eventARaised, eventBRaised;

    eventARaised = eventBRaised = false;

    EventPublisher publisher = new EventPublisher();

    publisher.EventA += delegate { eventARaised = true; };
    publisher.EventB += delegate { eventBRaised = true; };

    //Do some stuff to the state of publisher.
    publisher.RaiseA();
    Assert.IsTrue(eventARaised, "Event A raised.");

    //Do some stuff to the state of publisher.
    publisher.RaiseB();    
    Assert.IsTrue(eventBRaised, "Event B raised.");
}

Here we test that when we change state A followed by state B, event A and then event B is raised.

The implementation is a little different when we have a type that implements the INotifyPropertyChanged pattern:

public class PropertyChangedEventPublisher : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private int a, b;

    public int A
    {        
        set
        {
            this.a = value;
            this.PropertyChanged(this, new PropertyChangedEventArgs("A"));
        }
    }

    public int B
    {
        set
        {
            this.b = value;
            this.PropertyChanged(this, new PropertyChangedEventArgs("B"));
        }
    }
}

We can write unit tests against it as follows:

[Test]
public void EventSequenceTest()
{
    bool eventARaised, eventBRaised;

    eventARaised = eventBRaised = false;

    PropertyChangedEventPublisher publisher = new PropertyChangedEventPublisher();

    publisher.PropertyChanged += (sender, e) => 
    {
        if (e.PropertyName == "A")
        {
            eventARaised = true;
        }
        else if (e.PropertyName == "B")
        {
            eventBRaised = true;
        }
    };

    //Do some stuff to the state of publisher.
    publisher.A = 1;
    Assert.IsTrue(eventARaised, "Event A raised.");

    //Do some stuff to the state of publisher.
    publisher.B = 2;
    Assert.IsTrue(eventBRaised, "Event B raised.");
}

Matters are further complicated when we have a type that raises events asynchronously i.e. on multiple threads:

public class AsyncEventPublisher
{
    public event EventHandler EventA, EventB;

    public void RaiseA()
    {
        ThreadPool.QueueUserWorkItem(delegate { EventA(this, EventArgs.Empty); });
    }

    public void RaiseB()
    {
        ThreadPool.QueueUserWorkItem(delegate { EventB(this, EventArgs.Empty); });
    }
}

Our previous unit testing pattern no longer works as the test starts asserting the event raising conditions before our threads have raised the events. For the same test, but with asynchronous events, we need to introduce thread synchronization:

[Test]
public void AsyncEventSequenceTest()
{
    AsyncEventPublisher publisher = new AsyncEventPublisher();

    using (AutoResetEvent eventA = new AutoResetEvent(false))
    using (AutoResetEvent eventB = new AutoResetEvent(false))
    {
        publisher.EventA += delegate { eventA.Set(); };
        publisher.EventB += delegate { eventB.Set(); };

        //Do some stuff to the state of publisher.
        publisher.RaiseA();
        AssertEvent(eventA, 1000, "Event A failed.");

        //Do some stuff to the state of publisher.
        publisher.RaiseB();
        AssertEvent(eventB, 1000, "Event B failed.");
    }
}

private static void AssertEvent(WaitHandle waitHandle, int timeoutMS, string message)
{
    if (!waitHandle.WaitOne(timeoutMS))
    {
        message += " Timed out after waiting " + timeoutMS + "ms.";
        throw new TimeoutException(message);
    }
}

In this variation of the test we use AutoResetEvents to synchronize our test thread with our event threads, thus ensuring that we wait for the event threads to execute. If events are not raised, then we timeout and an exception fails the test.

My partner in dev Frederik was developing a file system monitoring component and found that we were both writing this sort of boilerplate unit testing code again and again. To simplify unit testing he started developing an event monitoring test harness which could be used to generalize event sequence testing for his component and reduce the boilerplate coding.

This series of posts will describe how a generic event monitor pattern can be used to allow much more straightforward unit tests of synchronous, asynchronous and INotifyPropertyChanged style events:

[Test]
public void EventTest()
{
    var publisher = new MyEventPublisher();

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

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

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

Or:

[Test]
public void EventTest()
{
    var publisher = new MyPropertyChangedEventPublisher();

    Action test = () => { publisher.X = 1; publisher.Y = 2; };

    var expectedSequence = new[] { "X", "Y" };

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

Event Sequence Unit Testing – Part 2


								

Leave a Reply