Testing

Beyond the Event Horizon: Event Complications

PB040015This article will continue to expand the capabilities of my reflection based event tests.  Although my VerifyEventCallbacks test method can inventory collections of EventHandler based events declared directly on a class, it only works on events backed by EventHandler delegates, and it won’t detect events declared on base classes.   To be truly useful, these shortcomings need to be addressed.

If you have read the previous articles in this series, then I hope you have gained a decent grasp of some fundamental event system concepts.  Here is a short table of contents for where we’ve been so far:

  1. Beyond the Event Horizon: Delegate Basics” — Explores the useful Delegate.GetInvocationListmethod.
  2. Beyond the Event Horizon: Event Basics” — Explains the relationship between Delegates and Events, and how the compiler implements simple events.
  3. Beyond the Event Horizon: Events You Don’t Own” — Shows how to use reflection to retrieve delegates for events declared on classes you can’t or won’t change.

For your enjoyment and education, you can can get the code associated with these articles from GitHub.  However, remember that this code is nothing more than a reimplementation of features already available in the ApprovalTests library, which is a free, open source library you can use to enhance your tests.  If your primary interest is to use these features, then don’t bother with cut-and-paste, just get yourself a copy of ApprovalTests from SourceForge or NuGet.

Don’t know what ApprovalTests are?  You will get more out of this article if you take a moment to watch a few videos in Llewellyn Falco’s ApprovalTests tutorial series on YouTube.

A Comprehensive Inventory

So far my tests against Poco look pretty nice. I tried to keep the test general, because it would be nice to reuse my extension methods on objects besides Poco instances. I’m worried that Poco doesn’t represent objects I might find in the real world.

Here are a couple traits of Poco that indicate it may not be complicated enough to model real world classes:

  1. Poco‘s events are only based on EventHandler. I should add some events based on EventHandler<T> or the always popular (hated?) PropertyChangedEventHandler.
  2. Poco‘s events are all declared on Poco. I should introduce a class that inherits from some of its events from a base class.

I’ll create a class with both these features by inheriting from Poco and implementing INotifyPropertyChanged on the descendant.

using System.ComponentModel;
public class PocoExtension : Poco, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        var handler = this.PropertyChanged;
        if (handler != null)
        {
            handler(sender, e);
        }
    }
}

And I’ll write a test for this class.

[TestMethod]
public void PocoExtensionTest()
{
    var target = new PocoExtension();
    target.ProcessStarted += Domain.HandleProcessStarted;
    target.PropertyChanged += Domain.HandlePropertyChanged;
    EventUtility.VerifyEventCallbacks(target);
}

As a developer using PocoExtension I can wire up a handler to ProcessStarted (which is inherited from Poco) just as easily as I can wire up a handler to PropertyChanged (which is declared on PocoExtension). Both events are part of the same object, so why should I need to worry about whether they are part of the same class? My intuition is that both events should show up in the inventory. Likewise, I wire up my handlers in the exact same manner, even though ProcessStarted and PropertyChanged leverage different delegate types to specify compatible handlers. They are both events, why should I have to worry about the delegate type? My intuition remains that both events should show up in the inventory. The test results do not meet my intuitive expectation:

Event callbacks for PocoExtension

With all the preamble in this article, it shouldn’t surprise you that neither event was found, but here is the final proof.  More importantly, I have a failing test that I can use to guide me toward a solution.  I have two problems to solve:

  1. Detect inherited events.
  2. Detect events not based on EventHandler

Which one to attack first?  The second requirement seems harder because all delegates inherit from MulticastDelegate whether they are associated with events or not.  It’s natural to think that EventHandler<T> derives from EventHandler but other than similar names (and sharing MulticastDelegate as a base type) these two delegates have no relationship.  Remember, delegates are their own types, and one of the rules for the delegate type is that they are all implicitly sealed (at the language level, the compiler can do what it likes when implementing delegates in IL).  So, there is no least derived type that I could use to find all the “event” delegates because none of them can even derive from each other in the first place.

So, without inheritance to lean on, I’ll need some other way to filter for delegates that are related to events. I’ll come back to this problem after dealing with the easier problem of detecting inherited events on PocoExtension.

Inherited Events

I wired up two event handlers in my test: ProcessStarted and PropertyChanged.  Neither was detected.  With ProcessStarted I know that the problem is not the delegate type, because it is backed by an EventHandler delegate.   So, the problem with this event must be inheritance.  In other words, my test does not detect ProcessStarted because it is declared on the base class (Poco).

Nothing has changed about the backing field I’m looking for, it’s still a private instance field on the declaring class.  Although the reflection API provides a BindingFlag that will flatten class hierarchies, private fields are not included, so this field is not showing up in my query.  I need to implement this capability myself.

The procedure seems straightforward.  Given an instance of Type, I can use the Type.BaseType property to get the less derived type.  I can crawl up this inheritance chain in a loop until it ends, collecting private fields as I go.  My guess is that I’m probably not the first developer to come up with this idea, and I wonder if maybe someone out there has a better solution than simple iteration.  However, after some research, it looks like everyone is just iterating so that’s what I’ll do.

Here is my current GetEventHandlers implementation.

public static IEnumerable<EventCallback> GetEventHandlers(this object value)
{
    if (value == null)
    {
        return null;
    }

    return from fieldInfo in value.GetType().GetFields(NonPublicInstance)
           where typeof(EventHandler).IsAssignableFrom(fieldInfo.FieldType)
           let callback = fieldInfo.GetValue<EventHandler>(value)
           where callback != null
           select new EventCallback(fieldInfo.Name, callback);
}

The problem is that GetFields does not include inherited private fields (“inherited private” is a weird thing to say—all that I mean is these fields exist at runtime and have values).  I can’t change the behavior of GetFields, so I need to replace it.  I’ll create an extension method on Type that does what I need:

public static IEnumerable<FieldInfo> EnumerateFieldsWithInherited(
    this Type typeInfo,
    BindingFlags bindingFlags)
{
    for (var type = typeInfo; type != null; type = type.BaseType)
    {
        foreach (var fieldInfo in type.GetFields(bindingFlags))
        {
            yield return fieldInfo;
        }
    }
}

This method is more or less implements the procedure described above, but instead of collecting the private fields, it streams them out as they are needed.  Now I can test whether updating GetEventHandlers to use this method will result in any changes to my results.

New results:

Event callbacks for PocoExtension

ProcessStarted
    [0] Void HandleProcessStarted(System.Object, System.EventArgs)

The test found the inherited event, excellent. My previous tests still pass, so I haven’t broken anything either. That’s one down, on to the next challenge.

Find events of any type

The current GetEventHandlers method does just what it advertises. It gets fields declared as EventHandler. Unfortunately, nothing requires that events be declared as EventHandler and there are many other options. Because delegates have no meaningful inheritance relationships, these other options don’t even inherit from EventHandler.

I’ll approach this problem by refactoring the out the “defective” part of GetEventHandlers. I’ll end up with two methods, GetEventHandlers will continue to work as advertised, but it will use a new method, GetEventsForType, for the heavy lifting.

public static IEnumerable<EventCallback> GetEventHandlers(this object value)
{
    return value.GetEventsForType(typeof(EventHandler));
}

public static IEnumerable<EventCallback> GetEventsForType(
    this object value, 
    Type type)
{
    if (value == null)
    {
        return null;
    }

    return from fieldInfo in value.GetType().EnumerateFieldsWithInherited(NonPublicInstance)
           where type.IsAssignableFrom(fieldInfo.FieldType)
           let callback = fieldInfo.GetValue<EventHandler>(value)
           where callback != null
           select new EventCallback(fieldInfo.Name, callback);
}

After making this change, my existing test on Poco still passes, and the output for my new test on PocoExtension is the same. Technically, PocoExtensionTest is failing, but that’s only because I haven’t approved anything yet. So this change hasn’t broken anything.

To get my test into a state where I can approve the result, I need to keep working on GetEventsForType.  In it’s current form, GetEventsForType lets me specify a type to filter for, but I know that PocoExtension uses more than one delegate type for its events.  I would rather pass a collection of delegate types to GetEventsForType (and pluralize the name).  Once I have a collection of types, I can change the query to collect backing fields for any type in the collection.

public static IEnumerable<EventCallback> GetEventsForTypes(
    this object value,
    params Type[] types)
{
    if (value == null)
    {
        return null;
    }

    return from fieldInfo in value.GetType().EnumerateFieldsWithInherited(NonPublicInstance)
            where types.Any(t => t == fieldInfo.FieldType)
            let callback = fieldInfo.GetValue<EventHandler>(value)
            where callback != null
            select new EventCallback(fieldInfo.Name, callback);
}

Keeping in mind that delegate inheritance is restricted, the old query’s use of IsAssignableFrom started to smell.  If there are no inheritance chains for delegates, then a simple equality check should suffice.  My existing tests are happy with this change, but PocoExtensionTest still doesn’t detect the PropertyChanged handlers.

The last piece of the puzzle is to create the collection of delegate types associated with PocoExtension’s backing fields and pass it to GetEventsForTypes.  I know the reflection API has GetProperties, GetConstructors, GetFields, why not GetEvents?  As a matter of fact, such a method exists. It returns an EventInfo array, and each member includes an EventHandlerType property that I can use to create a collection of types. Another extension method is in order, but naming it is hard.  GetEventHandlers still seems like the best name because its so general, but it implies a false relationship with the EventHandler delegate. So, I’ll go with Callback.

public static IEnumerable<EventCallback> GetEventCallbacks(
    this object value)
{
    var types = value.GetType().GetEvents()
        .Select(ei => ei.EventHandlerType).Distinct();
    return value.GetEventsForTypes(types.ToArray());
}

Notice that I don’t pass any binding flags to GetEvents. Events should be public. They could be protected or perhaps even private but that seems kind of weird, so I’m not going to worry too much about it. I update VerifyEventCallbacks to use this new method:

public static void VerifyEventCallbacks(object value)
{
        // ...
        foreach (var callback in value.GetEventCallbacks())
        {
            buffer.AppendLine(callback.ToString());
        }
        // ...
}

Making this change doesn’t break any passing tests, but it does “break” the test I haven’t approved yet.  Instead of showing me any results, that test now throws an InvalidCastException.  Turns out that I did not pay enough attention when I let my refactoring tool extract GetEventsForTypes.

Although the method takes an array of types, the query still attempts to cast everything to EventHandler.  Again, since inheritance relationships don’t exist between delegate types, PropertyChangedEventHandler can’t cast to EventHandler.  My only choices are to use Delegate or MulticastDelegate for my cast.  Delegate will work fine because the only thing I need to do is call GetInvocationList.

public static IEnumerable<EventCallback> GetEventsForTypes(
    this object value,
    params Type[] types)
{
    // ...
    return from fieldInfo in value.GetType().EnumerateFieldsWithInherited(NonPublicInstance)
            where types.Any(t => t == fieldInfo.FieldType)
            let callback = fieldInfo.GetValue<Delegate>(value)
            where callback != null
            select new EventCallback(fieldInfo.Name, callback);
}

Now my test makes it all the way to the call to Approvals.Verify and produces output:

Event callbacks for PocoExtension

PropertyChanged
    [0] Void HandlePropertyChanged(System.Object, System.ComponentModel.PropertyChangedEventArgs)

ProcessStarted
    [0] Void HandleProcessStarted(System.Object, System.EventArgs)

More importantly, it’s output that I can approve.  Cue Borat: “Great Success!”

Cleanup

Hopefully cleanup will be easier this time when compared to the “Making it Better” section in the last segment of this series.  But a little cleanup is necessary because once again GetType is called before making a null check, this time in GetEventCallbacks.

Here’s a test to detect the defect:

[TestMethod]
public void NullHasNoEventCallbacks()
{
    Assert.IsFalse(ReflectionUtility.GetEventCallbacks(null).Any());
}

For previous null-checks I used a if-null-return-null pattern to handle the null case.  However, as these null checks multiply I’m getting tired of writing the same code over and over again, simple as it may be.  So in this test I’m not going to look for null when null is returned, I’m going to look for an empty collection.  If I can come up with a NullObject solution to the null cases that I like, then I’ll refactor my null checks to use that instead of returning null.

I want a NullObject that is a do-nothing implementation of Type.  The NullType should respond to GetFields and GetEvents calls with empty arrays.  Before I run off an create one, I should see if the framework already has a Type that would work as a NullType.  Turns out that a suitable type does exist: typeof(void).  MSDN says System.Void is rarely useful in a typical application and that it is used by classes in System.Reflection.  I’m doing reflection, so I’ll use it too.  I just need to make sure that when I try to get a value’s type, that System.Void is used when that value is null.

public static Type GetType(object value)
{
    return value == null ? typeof(void) : value.GetType();
}

Notice that this method is not an extension method.  Now I change GetEventCallbacks to use the new method.  That gets me past my first null reference exception, but my test is still hitting a null reference when it tries to call Any on the results from GetEventCallbacks.  I need to follow the execution a little further an make sure that GetEventCallbacks never returns null.  (By the way Code Contracts would be a great way to help diagnose and resolve this type of issue, but I’ll just investigate it “by hand” for this example.)

Remember that GetEventsForTypes will return null when value is null.  I’ll update this method to use the new GetType method instead of a local null check and see what happens.

public static IEnumerable<EventCallback> GetEventsForTypes(
    this object value,
    params Type[] types)
{
    return from fieldInfo in GetType(value).EnumerateFieldsWithInherited(NonPublicInstance)
        // ...
}

NullHasNoEventCallbacks passes after making this update!  However, an older test, NullHasNoProcessCompletedHandler, fails now.  This test is expecting GetEventHandlers to return null, so I need to update its expectation.

[TestMethod]
public void NullHasNoProcessCompletedHandler()
{
    Assert.IsFalse(ReflectionUtility.GetEventHandlers(null).Any());
}

This new version of the test passes.  Now I’ll just search for any more null-checks and see if I can use the new method there.  The last candidate null check is in VerifyEventCallbacks.  I put a null check in there to make it safe to retrieve the type name.  If I use System.Void then instead of verifying an empty result I’ll get a result like this:

Event callbacks for Void

I’m on the fence on whether that is better than the empty result, but I lean toward the empty result.  I think that seeing an empty result is more likely to make me consider that I passed in a null value rather than seeing “Void”.  So I leave VerifyEventCallbacks alone.

Relationship with EventApprovals

Unfortunately these scenarios are not supported in ApprovalTests 2.0.  When I wrote EventApprovals, I needed to inventory the event handlers on a WinForms application.   Neither inheritance problems nor problems with delegate types surfaced while using EventApprovals against a WinForms target, because of the custom event implementation WinForms uses.

Eventually, when I used EventApprovals with a POCO class which extended an INotifyPropertyChanged implementer, I  encountered these problems and figured this stuff out.  The good news is that Llewellyn and I got together recently and these fixes have made their way upstream into ApprovalTests 2.0+.  As of this writing, you should compile ApprovalTests from source if you need these features immediately.  If you’re reading this later on, and you have ApprovalTests 2.1 or greater, then you already have these features.  Once you have a version of ApprovalTests with these fixes, you don’t have to do anything special.  EventApprovals.VerifyEvents takes advantage of them automatically.

In terms of implementation, the delegate type issue is solved almost identically to what I’ve shown here.  For inheritance, Llewellyn thought it would be fun to solve the problem with recursion, so that’s a little different.

Up Next

I’m feeling better about my tools now. To review, these extension methods can dynamically find all event-backing delegates, regardless of type. And they can find delegates no matter where they are declared in the class hierarchy. It looks like this set of extension methods can handle all my event testing needs for any object, but is that really so?

It turns out that there is large and important set of events which these methods will completely fail to find events for.  I’ve mentioned it a couple times already: Windows Forms.  I’ll take a look at WinForms events next time in: “Beyond the Event Horizon: WinForms Event System

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