Testing events with unit tests

Posted by Chris on March 13, 2007

How to test events seem to be a question that sometimes comes up regarding unit testing. There are three different things that we want to test when using events.

* Verify that an event subscriber really wires up a listener to an event
* Verify that an event subscriber does what it should when that event is fired
* Verify that an event publisher fires an event when it should

The first two are basically the same. If we can verify that some action that should be taken when an event is fired really does happen then we have of course also verified that the subscriber does listen to the event. But I prefer to start simple with my tests and therefore often end up with a small test like the following one for testing the wiring only:

using System;
using NUnit.Framework;

namespace TestingEvents
{
  [TestFixtureAttribute]
  public class EventsFiringTests
  {
    [Test]
    public void SubscriberWiresListenerToListenToMeEvent()
    {
      FakePublisher publisher = new FakePublisher();

      new SubscriberUnderTest(publisher);

      Assert.AreEqual(1, publisher.ListenToMeSubscriberCount);
    }
  }

  public interface Publisher
  {
    event EventHandler ListenToMe;
  }

  public class FakePublisher : Publisher
  {
    public event EventHandler ListenToMe;

    public int ListenToMeSubscriberCount
    {
      get { return ListenToMe.GetInvocationList().Length; }
    }

    public void FireListenToMe()
    {
      ListenToMe.Invoke(this, EventArgs.Empty);
    }
  }

  public class SubscriberUnderTest
  {
    private string message;

    public SubscriberUnderTest(Publisher publisher)
    {
      message = null;

      publisher.ListenToMe += IAmListening;
    }

    private void IAmListening(object sender, EventArgs args)
    {
      message = "I heard that!";
    }

    public string Message
    {
      get { return message; }
    }
  }
}

This test only tests that the subscriber actually does wire up a listener to the ListenToMe event of the publisher it is instantiated with. The next step is to verify what it does when the event is fired from the publisher. The next test takes care of that.

  [Test]
  public void MessageIsSetWhenListenToMeIsFired()
  {
    FakePublisher publisher = new FakePublisher();
    SubscriberUnderTest subscriber = new SubscriberUnderTest(publisher);

    publisher.FireListenToMe();

    Assert.AreEqual("I heard that!", subscriber.Message);
  }

That takes care of testing that a subscriber does what we expect it to when the event it subscribes to is fired. The final thing that needs to be tested is that a publisher actually fires an event when we expect it to. Lets add a new class, PublisherUnderTest, and another test.

  [Test]
  public void PublisherFiresListenToMeWhenAngry()
  {
    PublisherUnderTest publisher = new PublisherUnderTest();

    bool listenToMeWasCalled = false;
    publisher.ListenToMe += delegate { listenToMeWasCalled = true; };

    publisher.GetAngry();

    Assert.AreEqual(true, listenToMeWasCalled);
  }

  public class PublisherUnderTest : Publisher
  {
    public event EventHandler ListenToMe;

    public void GetAngry()
    {
      if (ListenToMe != null)
      {
        ListenToMe.Invoke(this, EventArgs.Empty);
      }
    }
  }

The test simply assigns an anonymous method to listen to the event from the publisher. In the anonymous method a boolean value is set to true to indicate that the event was fired.

Trackbacks

Trackbacks are closed.

  • Seth, I am not quite sure what you mean. In the example above there are two real types, SubscriberUnderTest and PublisherUnderTest. The tests verify functionality in those two types, only. The fakes are used as stubs and spies to drive and verify the behavior of the real types. Do you see things differently?

  • Seth Flowers

    Whats the point of unit testing _only_ mock objects. I expect to see at least one real type being tested, the rest being mocks and interfaces that the mocks implement. If all the code is in mock objects, the test is worthless.

    Sandimashighschoolfootballrules

blog comments powered by Disqus