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.

Back from SQL Server Open World 2007

Posted by Chris on March 11, 2007

From thursday evening until saturday afternoon I was at the SQL Server Open World in Denmark. SSOW turned out to be an excellent conference. There where about 140 attendees and speakers and there was a lot of interaction between people. Networking is always important and of course happens at all conferences, but I do believe that SSOW had a couple of things helping make networking very easy. How about these examples?

* During the first evening after the opening introduction and “technical” presentation they moved everyone into “the party house”. This was where the unfortunate latest employees of Miracle (the conference organizers) happened to live. Note that everyone means about a hundred people, in one small house meant for 4-8 people.
* The party house had free beer, brewed by Miracle themselves (or rather their sister company that is in the beer brewing business).
* Friday night was beach party. The conference was held at Lallandia, a vacation resort in Denmark with a big indoors waterland. Networking is just so much easier in a jacuzzi with a beer in your hand.
* Most conferences try for a 80/20 mixture of serious stuff (sessions) and fun stuff. At SSOW they go for a 50/50 mix. That does not mean there is less serious stuff, just a lot less time to sleep. :)

I gave two presentations. The first one was a completely new one called Working with SQL Server in an agile development environment (though I think I will change that to Agile Data Practices if I give it again). Even though there where only a few attendees I still had a good time, since it was easier to get a good discussion going.

The other presentation was Understanding CLR Integration which I did for the fourth time. I think it went very well, with a lot of good interaction from the audience. I think it was the atmosphere at the conference that encouraged this, I know I saw it in other presentations as well. Two specific things where mentioned in the talk, which I promised to address here:

* Regarding user-defined aggregates (UDAggs) that can be created as CLR objects, we discussed the problem of the 8000 bytes limitation for “MaxByteSize”:http://msdn2.microsoft.com/en-us/library/microsoft.sqlserver.server.sqluserdefinedaggregateattribute.maxbytesize.aspx. As I said, this limitation really makes it impossible to use UDAggs for a lot of cases where they would otherwise be an option, for instance the often used examples of calculating a median or concatening strings. However, the really interesting bit is that I posted a “feedback item regarding this”:http://connect.microsoft.com/SQLServer/feedback/ViewFeedback.aspx?FeedbackID=124559 to Microsoft Connect during the SQL Server 2005 beta testing period. What I said was that the maximum needs to be higher, possibly even indefinite. As the feedback item shows the response from Microsoft was that this was a reasonable request but since it was a non-trivial change it would not make it into SQL Server 2005 (RTM). Wednesday evening however this feedback item was closed and marked as fixed! Unfortunately it does not say how it was fixed or in which version (future service pack of SQL Server 2005, or Katmai the next version of SQL Server?) it is fixed. I am hoping that this gets clarified in the feedback item and also discussed it with Mark Souza from Microsoft who was there (with Lubor Kollar) as speakers and Microsoft representatives.
* The other thing was a question regarding the fact that not all system assemblies can be loaded in SQLCLR. The question was if the list of approved assemblies is available somewhere. I found “this blog post”:http://www.sqlskills.com/blogs/bobb/2006/05/28/NewSQLCLRapprovedAssemblyInSP1.aspx by “Bob Beauchemin”:http://www.sqlskills.com/blogs/bobb/ to be a good answer.

*Download the presentations* (as PDFs):

* Working with SQL Server in an agile development environment (with demo code)
* Understanding CLR Integration

All in all I had a great time. If you are a SQL Server speaker I would definitely recommend putting in an abstract for a session next year.