Testing

Beyond the Event Horizon: Event Basics

OLYMPUS DIGITAL CAMERAAt the heart of every .NET event is a Delegate.

In part one of this series: “Beyond the Event Horizon: Delegate Basics”, I introduced my motivation for writing EventApprovals (part of ApprovalTests 2.0).  I briefly touched on ApprovalTests basics (a woefully inadequate introduction, visit Llewellyn’s YouTube series for a thorough overview).  After this preamble, I covered the basic method for extracting invocation lists from delegates, then looked at some potential confusion which could be caused by the dual unicast/multicast nature of the .NET delegate implementation.

In this part of the series I’ll cover “simple” events or what I’ll often refer to as POCO events.  It may be helpful to define what I mean by POCO event, since it’s a term I made up.  To me a POCO event is what you get when you are writing your own class and you create an event like this:

public event EventHandler ProcessCompleted;

Declaring an event this way is similar to declaring an auto property, the compiler sees that you want to setup an event and generates a default implementation for you.  In this article I’ll cover what the compiler generates and how to test the compiler implementation.  In a later article we’ll see the custom event implementation which backs events in Windows Forms.

This article should cover some useful techniques, but remember that I’m re-implementing a system similar to EventApprovals, I’m not actually covering the internals of what made it into ApprovalTests.  The code for this demo is available on GitHub, but if you just need to do what this article describes, don’t waste your time with cut and paste, go straight to ApprovalTests and let it do the heavy lifting for you.

Simple Events

Before jumping headfirst into the .NET event implementation and the reflection required to retrieve the subscriber list, it is worthwhile to consider whether doing any of that is even necessary.  If a more simple testing approach can verify your requirements then you shouldn’t hesitate to use that approach.  Here, I’ll give an example of a scenario that doesn’t require reflection and consider when this approach is better replaced with a reflection-based approach.

Was the Event Raised When Expected?

Consider my simple business object Poco:

public class Poco
{
    public event EventHandler ProcessCompleted;

    public int DoWork()
    {
        // ...Do very hard work...
        OnProcessCompleted(this, EventArgs.Empty);
        return result;
    }

    protected virtual void OnProcessCompleted(object sender, EventArgs e)
    {
        EventHandler handler = ProcessCompleted;
        if (handler != null)
        {
            handler(sender, e);
        }
    }
}

In this scenario I want to ensure that Poco raises the process completed event when the process completes.  Assuming that the tasks Poco performs in DoWork are sufficiently amenable to mocking, then you could write a test that verified the call to OnProcessCompleted in isolation.

[TestMethod]
public void RaiseCompletedEventWhenProcessCompletes()
{
    bool raisedEvent = false;
    Poco poco = new Poco();
    poco.ProcessCompleted += (s, e) => raisedEvent = true;
    poco.DoWork();
    Assert.IsTrue(raisedEvent);
}

This test works because it only requires access to operations that are externally accessible. I create a flag and set it to false. Later, I’ll assert that the flag is true. The test will only pass if something in DoWork causes the flag to become true. I set this up by assigning an anonymous event handler to ProcessCompleted. If the event handler is invoked, then my flag changes, and the test passes. I don’t care if there are any other methods in the invocation list, I just care that my test handler has been invoked.

Testing Events Without Raising Events

I may not be able to raise the event so easily. The tasks in DoWork might not be amenable to mocking. The method might have undesirable side effects, dependencies which I can’t satisfy, or the task simply takes longer than I’m willing to allow when testing.   Lets assume that one of the conditions listed above is true for DoWork.  If this is the case, then I can’t test by raising the event.

If ensuring that DoWork raises the ProcessCompleted event is my only concern, then the invocation list can’t help me either.  Membership in the invocation list only indicates that some subscriber is listening to some event when its raised, but listening to an event doesn’t imply the event will ever be raised.

I know that Poco uses “POCO” events.  If I forget that I wrote Poco and begin to worry that Poco has a custom event implementation that doesn’t register handlers in the expected fashion, it might make sense to hook up a dummy handler then check the invocation list to see if my handler is there.  However, knowing that ProcessCompleted doesn’t hide any secrets, testing the invocation list would only prove that Delegate.Combine works correctly.  I usually trust the framework to have correct implementations. .

If ensuring that DoWork raises the event is part of a requirement that must be satisfied and delivered, then I need to refactor DoWork until it’s testable. I won’t cover that here, but you should check out Llewellyn Falco’s video on the “Peel” technique.

Ok, so there are some scenarios where raising the event is the best way to test.  When raising the event is impractical, there are still scenarios where you shouldn’t go after the subscriber list, because it won’t actually verify your requirements.  Is there any point in reading on?  Can’t I—the person who has wanted this functionality for 3 years—even come up with an example of when it’s useful?

Actually, I can describe a couple scenarios involving WinForms where this is very useful, but introducing WinForms into the equation without first understanding the basic technique would just introduce complications that would hurt your brain.  So I’ll dream up a scenario where this is useful without talking about WinForms and I’ll talk about WinForms later.

Start by assuming that Poco is not part of the top-level API, but instead is part of a “primitives” layer.  The purpose of the primitive API is to enable multiple programming models against the same functionality (MEF, for example, has a primitive layer).  Suppose I have a class called PocoClient that is part of a specific programming model built on top of the more primitive layer. Part of PocoClient‘s job is to hide details like the ProcessCompleted event. PocoClient must subscribe to the ProcessCompleted event and provide a default handler.

public class PocoClient
{
    private readonly Poco primitive;

    public PocoClient(Poco primitive)
    {
        this.primitive = primitive;
        this.primitive.ProcessCompleted += this.LogCompletionTime;
    }

    private void LogCompletionTime(object sender, EventArgs e)
    {
        // write to log...
    }
}

Now the question my test needs to ask is a little different. I’m still assuming that DoWork remains too expensive to call, but I want to know that PocoClient correctly wires itself up to Poco. Observing PocoClient‘s logger for activity is not an option, because DoWork is too expensive. However, if I could see Poco.ProcessCompleted‘s subscriber list, and test that LogCompletionTime is in the invocation list, then I could verify the requirement statically, without running any code other than the constructor.

Quick and Dirty Solution

At the heart of every event is a delegate.  Although you can use any delegate (including Func or Action) when declaring an event the most common practice is to use EventHandler, EventHandler<T> or some pre-defined delegate type like PropertyChangedEventHandler. At the language level, delegates are types, which means they are their own things (with their own rules) and they are peers with classes, structs and interfaces.  However, the compiler implements delegates as special classes that all derive from MulticastDelegate.  This can be confusing because delegates can share some behavior with classes in certain coding contexts.

I’ll start with my simple Poco class.

public class Poco
{
    public event EventHandler ProcessCompleted;
   // ... the rest of Poco ...
}

Poco has a “Field-like event” which is a horrible name for a concept which shouldn’t be hard to explain.  I alluded to this a few paragraphs ago, but here is an analogy:

"Field-like Event" : Delegate Field :: "Auto-Implemented Property" : Data Field

An automatically implemented property consists of a compiler generated field, and a pair of compiler generated methods (get and set).  A better name for “Field-like Event” might have been “Automatically-Implemented Event”.  At least we can be happy that they didn’t call autoproperties “Field-Like Properties”.

The Implementation

So, what do I get when I declare Poco this way?  I can use ILSpy to see what the compiler did in IL, but the C# view looks just like what I wrote in Visual Studio.  In this case Reflector can provide better insight if you drill into the event node.

public class Poco
{
    // Fields
    private EventHandler ProcessCompleted;

    // Events
    public event EventHandler ProcessCompleted
    {
        add
        {
            EventHandler handler2;
            EventHandler processCompleted = this.ProcessCompleted;
            do
            {
                handler2 = processCompleted;
                EventHandler handler3 = (EventHandler)Delegate.Combine(handler2, value);
                processCompleted = Interlocked.CompareExchange<EventHandler>(ref this.ProcessCompleted, handler3, handler2);
            }
            while (processCompleted != handler2);
        }

        remove
        {
            EventHandler handler2;
            EventHandler processCompleted = this.ProcessCompleted;
            do
            {
                handler2 = processCompleted;
                EventHandler handler3 = (EventHandler)Delegate.Remove(handler2, value);
                processCompleted = Interlocked.CompareExchange<EventHandler>(ref this.ProcessCompleted, handler3, handler2);
            }
            while (processCompleted != handler2);
        }
    }
}

There is a lot going on there for one line of code.  The good news is that a lot of it is ceremony around ensuring an atomic update to the backing field, and we don’t need to remember it.  I can simplify this code to something roughly equivalent, which just contains the parts that matter to the discussion at hand:

public class Poco
{
    // Fields
    private EventHandler ProcessCompleted;

    // Events
    public event EventHandler ProcessCompleted
    {
        add
        {
            this.ProcessCompleted = (EventHandler)Delegate.Combine(this.ProcessCompleted, value);
        }

        remove
        {
            this.ProcessCompleted = (EventHandler)Delegate.Remove(this.ProcessCompleted, value);
        }
    }
}

Once again, I’ll refer you to the autoproperty analogy.  The compiler gives us a backing field, and two methods.  One interesting (and useful) difference between the autoproperty implementation and the autoevent implementation is the name the compiler chooses for the backing field.  If the compiler used the same rules for autoevents as it uses for autoproperties, the declaration might look something like this:

private EventHandler <ProcessCompleted>k_BackingField;

However, instead of using this unspeakable and potentially un-guessable generated name, we have this declaration:

private EventHandler ProcessCompleted;

This name is much nicer because its easy to guess, it has the exact same name as the event.  Why do I like that this field’s name is easy to guess?  Simple, this field is just a reference to a delegate.  In other words, this field is the thing I want and although it is private and compiler-generated, guessing it’s identity is trivial.  I’ll file that useful fact away for now, and take a look at the compiler-generated add/remove methods.

Compiler Tricks – An Aside

I can’t help but comment on the expanded, Reflector-generated code shown above.  It doesn’t compile because the event and the field have the same name.  This is a trick the compiler can do when writing IL, but we can’t do when writing C#.  When the compiler requests the member field, it uses the ldfld IL instruction.  According to MSDN, this instruction loads the value of a field onto the stack.  It doesn’t load the value of a property, it doesn’t load the value of a local, it doesn’t load the value of an event (whatever that would mean).  It loads the value of a field.  So it seems that IL only cares that the backing field as a unique name among other fields, while C# insists that the name be unique among all members whether they are fields, properties, events, etc.

So C#’s rules appear more restrictive, which also prevents us from accidentally creating some unrelated field with the same name as our event and usurping the name the compiler wants to use.  I’m not sure why they chose to use unspeakable names when creating autoproperties, it seems like the same trick they used on events would have also worked for that feature.

The Add/Remove Methods

As you’ve seen, the compiler generates a few lines of code that use Delegate.Combine or Delegate.Remove (depending on which method you are looking at) to create a new Delegate, then atomically updates the backing field to point at the new delegate.  I could replace this implementation with whatever I want, as long as I follow the language rules enforced by C# (meaning, as long as I use a compatible backing field name).  Because anyone (including Microsoft) could decide to use a custom event implementation, you can’t assume that events will always be constructed this way.

For example, suppose I needed to create a unicast event.  Because all delegates are multicast by default, I would need to create custom add/remove events to implement an event that replaced its backing field rather than using Delegate.Combine to update the backing field.  (Of course, a mischievous reader might point out that this wouldn’t prevent a caller from passing in a multicast delegate).

On the other hand, most people probably need a reason to do extra work the compiler could do instead.  I think its safe to assume, that out in the universe, there are many, many compiler implemented events.

A Test that Doesn’t Work and Why

I’ll write a test that wires up a handler then see if I can get the invocation list.  Notice that I’m using DelegateUtility and Domain, which I created in part one of this article.

[TestMethod]
public void GetPocoEventInvocationList()
{
    Poco poco = new Poco();
    poco.ProcessCompleted += Domain.HandleProcessCompleted;

    EventHandler pocoDelegate = poco.ProcessCompleted;  // does not compile.
    DelegateUtility.VerifyInvocationList(pocoDelegate);
}

In case you missed the comment, this test wont compile. The compiler produces an error when we try to assign ProcessCompleted to pocoDelegate

The event ‘ProcessCompleted’ can only appear on the left hand side of += or -= (except when used from within the type ‘Poco’)

Not to beat a dead horse, but events are a lot like properties.  Remembering this helps us to make sense of the compiler error.  The local pocoDelegate is null, waiting to get a reference to a delegate.  If I have a compatible method, I can use the method group syntax to assign a value to pocoDelegate.  An event is just a pair of methods used to update a delegate (usually–they could do something weird or nothing at all).  Regardless of what they actually do, they are methods, why can’t we assign them to pocoDelegate?

For starters, the event represents a pair of methods.  How can the compiler determine which method to assign to the delegate?  The compiler can make the distinction between property getters and setters by looking at which side of the statement the property appears on—get appears on the right side, while set appears on the left.  One of the points that the compiler error makes is that events can only appear on the left-hand side of the += or –= operator.  The operator used determines which method you want when you try to use add or remove, but you can’t use those operators to help the compiler make a decision about which delegate to generate and assign to the local variable.  Finally, even if you could tell the compiler which method to use, it wouldn’t matter.  Neither add or remove can be assigned to the local variable: the methods do not have signatures compatible with EventHandler.

It all comes down to this: the only thing you can do with an event is choose between calling add or remove and the only way to make that choice is by writing down the appropriate operator.

Confusing Duality

Its easy to get confused about what an event is because the compiler/IDE is “helpful” in other ways. From within the class, I can still use += or -= (or AddHandler and RemoveHandler if you’re writing VB). But, inside the class I’m also allowed to use assignment in either direction:

var handler = this.ProcessCompleted;

The compiler can see that this statement doesn’t map in any logical way to add or remove. So, it helpfully treats the event like a property  (or a field if you prefer) and gets a reference to the invisible, private, backing field and puts it into handler. I’m not saying it is a property. It just looks a hell of a lot like one when you are writing code inside your class. Likewise, this statement feels a lot like a property setter:

this.ProcessCompleted = null;

This detaches ProcessCompleted‘s handler into the void, and is perfectly legal. So this is the what the second half of the compiler error means.

except when used from within the type ‘Poco’

The illegal statement that I made in my test would be perfectly legal if I did it from within the class. With that insight, I can think of a way to rewrite my class so that my test could run.  I’ll add a public method that exposes the delegate.

public class Poco
{
    public event EventHandler ProcessCompleted;

    public EventHandler GetProcessCompletedHandler()
    {
        return this.ProcessCompleted;
    }

    // ...

I’ll go ahead and admit up front that this is a degenerate solution with an odor of indecent exposure, but for now my interest is only whether it works. An update to the test should reveal the answer:

[TestMethod]
public void GetPocoEventInvocationList()
{
    Poco poco = new Poco();
    poco.ProcessCompleted += Domain.HandleProcessCompleted;

    EventHandler pocoDelegate = poco.GetProcessCompletedHandler();  
    DelegateUtility.VerifyInvocationList(pocoDelegate);
}

This test compiles and runs, so far–so good. Here are the results:

[0] = Void HandleProcessCompleted(System.Object, System.EventArgs)

While exposing the event handler hardly qualifies as a best-practice, I can see that the test works as expected. I can verify that the functionality in PocoClient is implemented correctly without attempting to call DoWork. It’s quick and dirty, emphasis on the dirty. But I’m not done, because this quick and dirty method only works when I control both ends of the subscription. What if I don’t?

If you don’t control the class the class the event is declared on, you’ll have to use reflection to get your hand on the delegate.  This will be the subject of the next post in this series.

Relationship with EventApprovals

There isn’t a strong relationship between this quick and dirty technique and what you will find in EventApprovals.  Exposing the delegate is only necessary if you don’t know how to retrieve it with reflection, EventApprovals uses the reflection approach, and you can too by calling ApprovalTests.Events.EventApprovals.VerifyEvents.

Up Next

The next article in this series will be “Beyond the Event Horizon: Events You Don’t Own”.  That article will cover how to retrieve event handlers using reflection and then expand the technique to include taking a complete inventory of an object’s events.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s