Clarify Your Test Intention with ApprovalTests

In this post I’m going to explore the benefits of shorter tests. Today, I’m not interested in shortening test run times (although that’s a good thing too). Instead, I am interested in shortening the amount of code I have to read before I can figure out (or remember) the intention of a test.

If you believe, even a little bit, that "the tests are the documentation" or "the tests are the spec", then the tests better be crystal clear about what they are trying to prove. If they are not clear then the "specification" aspect of the test will be lost to future readers (possibly yourself).

So lets look at one way intention gets less clear when tests are long.

Long Test

While engaged in some practical refactoring at work, I recently came across some really long tests. The general domain was parsing, but I’ve changed the specifics to protect the guilty. I’m pasting the complete test here because I want to give you a taste of how overwhelming the initial test looked.

namespace ApprovalsExample
{
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    /// <summary>
    /// Describe a JSON parser.
    /// </summary>
    [TestClass]
    public class JsonParserTest
    {
        /// <summary>
        /// Parse this JSON into a POCO object.
        /// </summary>
        [TestMethod]
        public void ItConvertsJsonToPoco()
        {
            const string Source = @"{
            ""status"": ""upcoming"",
            ""visibility"": ""public"",
            ""maybe_rsvp_count"": 0,
            ""venue"": {
                ""id"": 11835602,
                ""zip"": ""92660"",
                ""lon"": -117.867828,
                ""repinned"": false,
                ""name"": ""TEKsystems"",
                ""state"": ""CA"",
                ""address_1"": ""100 Bayview Circle #3400"",
                ""lat"": 33.655819,
                ""city"": ""Newport Beach"",
                ""country"": ""us""
            },
            ""id"": ""124139172"",
            ""utc_offset"": -25200000,
            ""duration"": 10800000,
            ""time"": 1378947600000,
            ""waitlist_count"": 0,
            ""announced"": false,
            ""updated"": 1370985561000,
            ""yes_rsvp_count"": 7,
            ""created"": 1370985561000,
            ""event_url"": ""http://www.meetup.com/vNext-OrangeCounty/events/124139172/"",
            ""description"": ""<p><strong>Talk Info :</strong></p>\n<p>The techniques for building applications have changed dramatically in the last <br />\n\nfew years. Gone are the days of single-tier, battle-ship gray, boring user <br />\n\ninterfaces. Users demand that your applications (or portions) run on more than <br />\n\none device. This session will take you on a tour of how you should be architecting your application by breaking it up into services. You will learn how <br />\n\nto create your business rules and data layer as a service. This seminar will <br />\n\nassume you have some knowledge of .NET but have been developing <br />\n\napplications the old way and you are now looking to see how to use WCF and <br />\n\nthe Model-View-View-Model (MVVM) design pattern to create applications that <br />\n\ncan be run one more than one user interface platform. This session has many <br />\n\ndemonstrations and you will be led step-by-step through the code. You will walk <br />\n\naway with a sample set of services that run on Silverlight, Windows Forms, <br />\n\nWPF, Windows Phone and ASP.NET.</p>\n<p> </p>\n<p><strong>About The Speaker</strong></p>\n<p>Paul D. Sheriff is the President of PDSA, Inc. (www.pdsa.com), a Microsoft <br />\n\nPartner in Southern California. Paul acts as the Microsoft Regional Director for <br />\n\nSouthern California assisting the local Microsoft offices with several of their <br />\n\nevents each year and being an evangelist for them. Paul has authored literally <br />\n\nhundreds of books, webcasts, videos and articles on .NET, WPF, Silverlight, <br />\n\nWindows Phone and SQL Server. Paul can be reached via email at <br />\n\nPSheriff@pdsa.com. Check out Paul's new code generator 'Haystack' at <br />\n\n<a href=\""http://www.CodeHaystack.com\"">www.CodeHaystack.com</a>.</p>"",
            ""how_to_find_us"": ""Office is on the 3rd floor of the North Tower - Occupied by TekSystems"",
            ""name"": ""Paul D. Sheriff - Architecting Applications for Multiple User Interfaces"",
            ""headcount"": 0,
            ""group"": {
                ""id"": 2983232,
                ""group_lat"": 33.650001525878906,
                ""name"": ""vNext_OC"",
                ""group_lon"": -117.58999633789062,
                ""join_mode"": ""open"",
                ""urlname"": ""vNext-OrangeCounty"",
                ""who"": ""Members""
            }
        }";

            var o = Event.DeserializeJson(Source);
            const string Answer = @"Announced: False, Created: 1370985561000, Description: <p><strong>Talk Info :</strong></p>
<p>The techniques for building applications have changed dramatically in the last <br />

few years. Gone are the days of single-tier, battle-ship gray, boring user <br />

interfaces. Users demand that your applications (or portions) run on more than <br />

one device. This session will take you on a tour of how you should be architecting your application by breaking it up into services. You will learn how <br />

to create your business rules and data layer as a service. This seminar will <br />

assume you have some knowledge of .NET but have been developing <br />

applications the old way and you are now looking to see how to use WCF and <br />

the Model-View-View-Model (MVVM) design pattern to create applications that <br />

can be run one more than one user interface platform. This session has many <br />

demonstrations and you will be led step-by-step through the code. You will walk <br />

away with a sample set of services that run on Silverlight, Windows Forms, <br />

WPF, Windows Phone and ASP.NET.</p>
<p> </p>
<p><strong>About The Speaker</strong></p>
<p>Paul D. Sheriff is the President of PDSA, Inc. (www.pdsa.com), a Microsoft <br />

Partner in Southern California. Paul acts as the Microsoft Regional Director for <br />

Southern California assisting the local Microsoft offices with several of their <br />

events each year and being an evangelist for them. Paul has authored literally <br />

hundreds of books, webcasts, videos and articles on .NET, WPF, Silverlight, <br />

Windows Phone and SQL Server. Paul can be reached via email at <br />

PSheriff@pdsa.com. Check out Paul's new code generator 'Haystack' at <br />

<a href=""http://www.CodeHaystack.com"">www.CodeHaystack.com</a>.</p>, Duration: 10800000, EventUrl: , Group: ApprovalsExample.Group, HowToFindUs: , Headcount: 0, Id: 124139172, MaybeRsvpCount: 0, Name: Paul D. Sheriff - Architecting Applications for Multiple User Interfaces, Status: upcoming, Time: 1378947600000, Updated: 1370985561000, UtcOffset: 0, Venue: ApprovalsExample.Venue, Visibility: public, WaitlistCount: 0, YesRsvpCount: 0";
            Assert.AreEqual(Answer, o.ToString());
        }
    }
}

We can guess from the initial JSON blob (grabbed from vNext OC‘s meetup.com event stream) and the test name that the intention is to demonstrate something about converting JSON into .net objects. But the input data for the test is so large that we must scroll almost an entire page before seeing the first executable line of code:

var o = Event.DeserializeJson(Source);

Once we get past the first 40 or so lines, we finally see that the Event class does the parsing. Next we have 40 or so lines of expectation definition before we reach a very simple assert:

Assert.AreEqual(Answer, o.ToString());

So the test is not that hard to understand, but the signal-to-noise ratio is wimpy: 2:83. In this high level test, the specifics of the JSON source are not important. The only important thing about the source text is that it produces the expected result. Likewise the only important thing about the expected result is that it is correct and it corresponds to the provided input. So, both giant strings are noise.

Alternatives

Of course, the thrust of my argument is that ApprovalTests provides the best set of tools for cleaning up a test like this. But let me setup a couple of straw-men first.

AAA

Maybe you read the test and thought, "Fool! You didn’t follow triple-A!" While it is true that the test doesn’t conform to the Arrange/Act/Assert pattern, making it conform to AAA only yields a small improvement. By moving the call to DeserializeJson from line 42 to line 83, I now conform to the pattern:

[TestMethod]
public void ItConvertsJsonToPoco()
{
    const string Source = @"{
    /* 80 lines of "Arrange" omitted */

    var o = Event.DeserializeJson(Source);
    Assert.AreEqual(Answer, o.ToString());
}

What is the improvement? Well now all the code is next to each other, so you no longer have to hunt for the "Act" part, just skip to the bottom, and there it is. Knowing where things should be is one of the strengths of AAA, I’ll concede that. Unfortunately, we haven’t done anything to fix the signal-to-noise ratio, it is still 2:83. It’s a little easier to find the signal, because its all bunched up at the end of the noise (past 2 pages of noise now).

Move the Noise

To gain any traction on the signal to noise ratio, we need to put the noise somewhere else.

Many of testers labor under a misconception similar to this: "A test must not interact with any system except the one under test." Many usually include the file system under the category "any". Clearly, I am not a subscriber to this line of thinking, but I can take a jab at the straw-man by pointing out that the tests exist in code files that live on the file system. So, I would not worry about that, but since so many do, lets see what kind of improvement we can get by moving things around. We could promote the big strings to fields, and reduce the number of lines in our test body.

[TestMethod]
public void ItConvertsJsonToPoco()
{
    var o = Event.DeserializeJson(Source);
    Assert.AreEqual(Answer, o.ToString());
}

This certainly makes this one test look nicer. If I only consider the test method we have fantastic signal-to-noise: 1:1. This is not to say that it is absolutely clear what this test intends to prove, but we can very quickly see how it tries to prove it. So, good signal-to-noise isn’t everything, but it helps.

Can we stop here and call it a day? Of course, the truth is that you can, because the test still passes. Not surprisingly though, I say no.

I have problems with this approach. In this example, I’ve only written one test, and this solution seems to work OK, but does it scale? At work, the actual test suite contained many tests, and this solution would not scale well. Applying "move the noise" to all the tests would result in half-a-dozen "sources" and half-a-dozen "answers". These were varying lengths, some much longer than 40 lines, so we are talking about a preamble of many hundred lines of "Arrange" starting off the class before we get to any "Act" or "Assert".

I also have a problem with maintaining giant strings inside the tests, no matter where they are put in the code. First, you often run afoul of newlines and quote marks. The newlines in the answer conform to the new lines in your environment, in my case this means CRLF. The JSON blob has a mixture of line endings, so something must be done to the answer or the source to get them to match. Then we have quote marks. The JSON uses double quotes, so I had to convert them to double-double quotes to make the multi-line string literal work. Of course I could have escaped everything and used a normal interpolated string… but that’s work too. I don’t want to do any extra work.

Giant strings in the code are also very easy to mess up. If you are clumsy like me (or maybe you are perfect… but you might have someone clumsy on your team) your cursor often ends up where you least expect it when you’re in the middle of typing a tweet to your sweet old grandmother (that’s what twitter is for right?). Next thing your know, your test is failing because some how the phrase "I really liked the pie @Grandma" ends up in your giant string. I don’t like constructing my tests in such a way that debugging sessions can result from dumb mistakes.

Use ApprovalTests to Hide the Noise

ApprovalTests for .net is a assertion library that enhances your existing test framework with new capabilities for long strings, dictionaries, collections, log files, web pages, WinForm views, WPF views, Entity Framework queries, event configurations, and RDLC reports. If this is the first you’ve ever heard of ApprovalTests, then I encourage you to explore further by watching a few short videos on youtube, posted by the creator of ApprovalTests, Llewellyn Falco. Don’t let the purple hair put you off, they are great videos.

ApprovalTests provide the perfect solution for shortening the long test presented at the beginning of this post. In fact, that test’s original author had essentially re-invented approval testing without knowing it, and without gaining the power that the ApprovalTests library would provide. Our test has three parts, a known input, an action, and a known correct output. The output is the big answer string, and we know it is correct because the test passed when I inherited it from its original author. Approval testing is about capturing human intelligence, a human has declared This is what the DeserializeJson method produces. We should continue to check that the correct answer is given. An approval test automates this check.

In particular, the ApprovalTests library not only automates this check for us, but provides us with better feedback on failure. It also hides the noisy strings most of the time, but will present us with an opportunity to review or update the answer when the test fails.

At work I refactored the original test into an ApprovalTest, but for this post, I’ll just continue from where we were. I’ll post all the code so you can watch it shrink. So here is where we we want to go:

namespace ApprovalsExample
{
    using System.IO;
    using ApprovalTests;
    using ApprovalTests.Reporters;
    using ApprovalUtilities.Utilities;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    /// <summary>
    /// Describe a JSON parser.
    /// </summary>
    [TestClass]
    public class JsonParserTest
    {
        /// <summary>
        /// Parse this JSON into a POCO object.
        /// </summary>
        [TestMethod]
        [UseReporter(typeof(VisualStudioReporter))]
        public void ItConvertsJsonToPoco()
        {
            var text = File.ReadAllText(PathUtilities.GetAdjacentFile("sample.json"));
            var o = Event.DeserializeJson(text);
            Approvals.Verify(o);
        }
    }
}

And here is where we are after "moving the noise":

[TestClass]
public class JsonParserTest
{
    private const string Expected = @"Announced: False, Created: 1370985561000, Description: <p><strong>Talk Info :</strong></p>
<p>The techniques for building applications have changed dramatically in the last <br />

few years. Gone are the days of single-tier, battle-ship gray, boring user <br />

interfaces. Users demand that your applications (or portions) run on more than <br />

one device. This session will take you on a tour of how you should be architecting your application by breaking it up into services. You will learn how <br />

to create your business rules and data layer as a service. This seminar will <br />

assume you have some knowledge of .NET but have been developing <br />

applications the old way and you are now looking to see how to use WCF and <br />

the Model-View-View-Model (MVVM) design pattern to create applications that <br />

can be run one more than one user interface platform. This session has many <br />

demonstrations and you will be led step-by-step through the code. You will walk <br />

away with a sample set of services that run on Silverlight, Windows Forms, <br />

WPF, Windows Phone and ASP.NET.</p>
<p> </p>
<p><strong>About The Speaker</strong></p>
<p>Paul D. Sheriff is the President of PDSA, Inc. (www.pdsa.com), a Microsoft <br />

Partner in Southern California. Paul acts as the Microsoft Regional Director for <br />

Southern California assisting the local Microsoft offices with several of their <br />

events each year and being an evangelist for them. Paul has authored literally <br />

hundreds of books, webcasts, videos and articles on .NET, WPF, Silverlight, <br />

Windows Phone and SQL Server. Paul can be reached via email at <br />

PSheriff@pdsa.com. Check out Paul's new code generator 'Haystack' at <br />

<a href=""http://www.CodeHaystack.com"">www.CodeHaystack.com</a>.</p>, Duration: 10800000, EventUrl: , Group: ApprovalsExample.Group, HowToFindUs: , Headcount: 0, Id: 124139172, MaybeRsvpCount: 0, Name: Paul D. Sheriff - Architecting Applications for Multiple User Interfaces, Status: upcoming, Time: 1378947600000, Updated: 1370985561000, UtcOffset: 0, Venue: ApprovalsExample.Venue, Visibility: public, WaitlistCount: 0, YesRsvpCount: 0";

    private const string Source = @"{
        ""status"": ""upcoming"",
        ""visibility"": ""public"",
        ""maybe_rsvp_count"": 0,
        ""venue"": {
            ""id"": 11835602,
            ""zip"": ""92660"",
            ""lon"": -117.867828,
            ""repinned"": false,
            ""name"": ""TEKsystems"",
            ""state"": ""CA"",
            ""address_1"": ""100 Bayview Circle #3400"",
            ""lat"": 33.655819,
            ""city"": ""Newport Beach"",
            ""country"": ""us""
        },
        ""id"": ""124139172"",
        ""utc_offset"": -25200000,
        ""duration"": 10800000,
        ""time"": 1378947600000,
        ""waitlist_count"": 0,
        ""announced"": false,
        ""updated"": 1370985561000,
        ""yes_rsvp_count"": 7,
        ""created"": 1370985561000,
        ""event_url"": ""http://www.meetup.com/vNext-OrangeCounty/events/124139172/"",
        ""description"": ""<p><strong>Talk Info :</strong></p>\n<p>The techniques for building applications have changed dramatically in the last <br />\n\nfew years. Gone are the days of single-tier, battle-ship gray, boring user <br />\n\ninterfaces. Users demand that your applications (or portions) run on more than <br />\n\none device. This session will take you on a tour of how you should be architecting your application by breaking it up into services. You will learn how <br />\n\nto create your business rules and data layer as a service. This seminar will <br />\n\nassume you have some knowledge of .NET but have been developing <br />\n\napplications the old way and you are now looking to see how to use WCF and <br />\n\nthe Model-View-View-Model (MVVM) design pattern to create applications that <br />\n\ncan be run one more than one user interface platform. This session has many <br />\n\ndemonstrations and you will be led step-by-step through the code. You will walk <br />\n\naway with a sample set of services that run on Silverlight, Windows Forms, <br />\n\nWPF, Windows Phone and ASP.NET.</p>\n<p> </p>\n<p><strong>About The Speaker</strong></p>\n<p>Paul D. Sheriff is the President of PDSA, Inc. (www.pdsa.com), a Microsoft <br />\n\nPartner in Southern California. Paul acts as the Microsoft Regional Director for <br />\n\nSouthern California assisting the local Microsoft offices with several of their <br />\n\nevents each year and being an evangelist for them. Paul has authored literally <br />\n\nhundreds of books, webcasts, videos and articles on .NET, WPF, Silverlight, <br />\n\nWindows Phone and SQL Server. Paul can be reached via email at <br />\n\nPSheriff@pdsa.com. Check out Paul's new code generator 'Haystack' at <br />\n\n<a href=\""http://www.CodeHaystack.com\"">www.CodeHaystack.com</a>.</p>"",
        ""how_to_find_us"": ""Office is on the 3rd floor of the North Tower - Occupied by TekSystems"",
        ""name"": ""Paul D. Sheriff - Architecting Applications for Multiple User Interfaces"",
        ""headcount"": 0,
        ""group"": {
            ""id"": 2983232,
            ""group_lat"": 33.650001525878906,
            ""name"": ""vNext_OC"",
            ""group_lon"": -117.58999633789062,
            ""join_mode"": ""open"",
            ""urlname"": ""vNext-OrangeCounty"",
            ""who"": ""Members""
        }
    }";

    /// <summary>
    /// Parse this JSON into a POCO object.
    /// </summary>
    [TestMethod]
    public void ItConvertsJsonToPoco()
    {
        var o = Event.DeserializeJson(Source);
        Assert.AreEqual(Expected, o.ToString());
    }
}

Lets start refactoring.

Hide Source in File

After adding ApprovalTests to the project using nuget, I can take advantage of ApprovalUtilities to help me move the big source string into a file that sits next to the code file. I could do this by making a file and using cut and paste, but as I previously discussed, I had to mangle the source with double-double quotes to make the string literal work. I could demangle the source by hand, but letting the computer do it will be quick and less error prone.

Here’s the relevant portions of the code:

namespace ApprovalsExample
{
    using System.IO;
    using ApprovalUtilities.Utilities;

    /// <summary>
    /// Describe a JSON parser.
    /// </summary>
    [TestClass]
    public class JsonParserTest
    {
        /* Giant strings still here, omitted for clarity */

        /// <summary>
        /// Parse this JSON into a POCO object.
        /// </summary>
        [TestMethod]
        public void ItConvertsJsonToPoco()
        {
            File.WriteAllText(PathUtilities.GetAdjacentFile("sample.json"), Source);
            var o = Event.DeserializeJson(Source);
            Assert.AreEqual(Expected, o.ToString());
        }
    }
}

I added a couple of namespaces that I will need going forward, and added a line of code to write the giant source string into a file. Notice that I am still using the giant source string in the test. I’m just going to change one thing at a time as I refactor, then run the tests before making the next change. The next time I run this test, PathUtilities will provide the full path to a non-existent file next to the code file called "sample.json". Then WriteAllText will create that file by dumping the giant source string into it. So I run the test, it passes, and now I have a copy of the source in "sample.json":

{
        "status": "upcoming",
        "visibility": "public",
        "maybe_rsvp_count": 0,
        "venue": {
            "id": 11835602,
            "zip": "92660",
            "lon": -117.867828,
            "repinned": false,
            "name": "TEKsystems",
            "state": "CA",
            "address_1": "100 Bayview Circle #3400",
            "lat": 33.655819,
            "city": "Newport Beach",
            "country": "us"
        },
        "id": "124139172",
        "utc_offset": -25200000,
        "duration": 10800000,
        "time": 1378947600000,
        "waitlist_count": 0,
        "announced": false,
        "updated": 1370985561000,
        "yes_rsvp_count": 7,
        "created": 1370985561000,
        "event_url": "http://www.meetup.com/vNext-OrangeCounty/events/124139172/",
        "description": "<p><strong>Talk Info :</strong></p>\n<p>The techniques for building applications have changed dramatically in the last <br />\n\nfew years. Gone are the days of single-tier, battle-ship gray, boring user <br />\n\ninterfaces. Users demand that your applications (or portions) run on more than <br />\n\none device. This session will take you on a tour of how you should be architecting your application by breaking it up into services. You will learn how <br />\n\nto create your business rules and data layer as a service. This seminar will <br />\n\nassume you have some knowledge of .NET but have been developing <br />\n\napplications the old way and you are now looking to see how to use WCF and <br />\n\nthe Model-View-View-Model (MVVM) design pattern to create applications that <br />\n\ncan be run one more than one user interface platform. This session has many <br />\n\ndemonstrations and you will be led step-by-step through the code. You will walk <br />\n\naway with a sample set of services that run on Silverlight, Windows Forms, <br />\n\nWPF, Windows Phone and ASP.NET.</p>\n<p> </p>\n<p><strong>About The Speaker</strong></p>\n<p>Paul D. Sheriff is the President of PDSA, Inc. (www.pdsa.com), a Microsoft <br />\n\nPartner in Southern California. Paul acts as the Microsoft Regional Director for <br />\n\nSouthern California assisting the local Microsoft offices with several of their <br />\n\nevents each year and being an evangelist for them. Paul has authored literally <br />\n\nhundreds of books, webcasts, videos and articles on .NET, WPF, Silverlight, <br />\n\nWindows Phone and SQL Server. Paul can be reached via email at <br />\n\nPSheriff@pdsa.com. Check out Paul's new code generator 'Haystack' at <br />\n\n<a href=\"http://www.CodeHaystack.com\">www.CodeHaystack.com</a>.</p>",
        "how_to_find_us": "Office is on the 3rd floor of the North Tower - Occupied by TekSystems",
        "name": "Paul D. Sheriff - Architecting Applications for Multiple User Interfaces",
        "headcount": 0,
        "group": {
            "id": 2983232,
            "group_lat": 33.650001525878906,
            "name": "vNext_OC",
            "group_lon": -117.58999633789062,
            "join_mode": "open",
            "urlname": "vNext-OrangeCounty",
            "who": "Members"
        }
    }

Admittedly, the indentation is a little funky, but at least all the double-double quotes are now back to single double quotes. A trip to JSONLint shows the blob is kosher. Now I can refactor the test to use this file instead of the giant string. Only two lines need to change:

var text = File.ReadAllText(PathUtilities.GetAdjacentFile("sample.json"));
var o = Event.DeserializeJson(text);

I changed WriteAllText to ReadAllText, then captured the result in a variable. Next, I updated the call to DeserializeJson to use the text I just read, instead of the string stored in Source. I run the test and it passes.

Now my refactoring tool tells me that the Source field is unused. So I delete the giant string and run the test. It passes, leaving me with the same test, minus about 40 lines of string.

namespace ApprovalsExample
{
    using System.IO;
    using ApprovalUtilities.Utilities;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    /// <summary>
    /// Describe a JSON parser.
    /// </summary>
    [TestClass]
    public class JsonParserTest
    {
        private const string Expected = @"Announced: False, Created: 1370985561000, Description: <p><strong>Talk Info :</strong></p>
<p>The techniques for building applications have changed dramatically in the last <br />

few years. Gone are the days of single-tier, battle-ship gray, boring user <br />

interfaces. Users demand that your applications (or portions) run on more than <br />

one device. This session will take you on a tour of how you should be architecting your application by breaking it up into services. You will learn how <br />

to create your business rules and data layer as a service. This seminar will <br />

assume you have some knowledge of .NET but have been developing <br />

applications the old way and you are now looking to see how to use WCF and <br />

the Model-View-View-Model (MVVM) design pattern to create applications that <br />

can be run one more than one user interface platform. This session has many <br />

demonstrations and you will be led step-by-step through the code. You will walk <br />

away with a sample set of services that run on Silverlight, Windows Forms, <br />

WPF, Windows Phone and ASP.NET.</p>
<p> </p>
<p><strong>About The Speaker</strong></p>
<p>Paul D. Sheriff is the President of PDSA, Inc. (www.pdsa.com), a Microsoft <br />

Partner in Southern California. Paul acts as the Microsoft Regional Director for <br />

Southern California assisting the local Microsoft offices with several of their <br />

events each year and being an evangelist for them. Paul has authored literally <br />

hundreds of books, webcasts, videos and articles on .NET, WPF, Silverlight, <br />

Windows Phone and SQL Server. Paul can be reached via email at <br />

PSheriff@pdsa.com. Check out Paul's new code generator 'Haystack' at <br />

<a href=""http://www.CodeHaystack.com"">www.CodeHaystack.com</a>.</p>, Duration: 10800000, EventUrl: , Group: ApprovalsExample.Group, HowToFindUs: , Headcount: 0, Id: 124139172, MaybeRsvpCount: 0, Name: Paul D. Sheriff - Architecting Applications for Multiple User Interfaces, Status: upcoming, Time: 1378947600000, Updated: 1370985561000, UtcOffset: 0, Venue: ApprovalsExample.Venue, Visibility: public, WaitlistCount: 0, YesRsvpCount: 0";

        /// <summary>
        /// Parse this JSON into a POCO object.
        /// </summary>
        [TestMethod]
        public void ItConvertsJsonToPoco()
        {
            var text = File.ReadAllText(PathUtilities.GetAdjacentFile("sample.json"));
            var o = Event.DeserializeJson(text);
            Assert.AreEqual(Expected, o.ToString());
        }
    }
}

Hide Expectation in File

I could use a similar technique to hide the expectation in a file, but I don’t need to because hiding the expectation is built into the library. This is one of the tasks that ApprovalTests excels at. So, leaving all else the same, I will add a couple namespaces to the code, and make a couple small changes to the test.

namespace ApprovalsExample
{        
    using ApprovalTests;
    using ApprovalTests.Reporters;
    /* Other namespace imports remain the same */

    /// <summary>
    /// Describe a JSON parser.
    /// </summary>
    [TestClass]
    public class JsonParserTest
    {
        private const string Expected = @"Announced: False, Created: 1370985561000, Description: ..."
        /* this giant string remains here for now */

        /// <summary>
        /// Parse this JSON into a POCO object.
        /// </summary>
        [TestMethod]
        [UseReporter(typeof(VisualStudioReporter))]
        public void ItConvertsJsonToPoco()
        {
            var text = File.ReadAllText(PathUtilities.GetAdjacentFile("sample.json"));
            var o = Event.DeserializeJson(text);
            Assert.AreEqual(Expected, o.ToString());
            Approvals.Verify(o);
        }
    }
}

I run this test and it fails, but this failure now occurs after the Assert, when I make the call to Verify. This is expected behavior for ApprovalTests. Until I have approved my expected output, ApprovalTests cannot check it for me, so it must continue to fail until I give my blessing to something. Besides failing, it also gives me the opportunity to review the results by launching a reporter. In this case, the output appears in Visual Studio’s diff viewer because I specified the VisualStudioReporter when I attached the UseReporter attribute to the test method.

The output we see on the lefts side is simply the result of converting the instance o into a string. Event happens to have a decent ToString formatting method, but I could have manipulated the output by formatting or redacting the data before calling Verify. Now the only question is whether I should give this result my blessing.

In fact, its not a question at all, I know that I can immediately approve the output because the original test still passes. Although the test shows as a failure in the test runner, I can see that it failed when it reached the Approval, meaning the Assert still passed. Since the assert is checking the same output that Verify checks, then if the Assert is good, the output received by Verify must also be good. Visual Studio does not provide merging unless you are connected TFS (as far as I can tell) so my options for approval are:

  1. Select all the left side and copy/paste to the right side.
  2. Use file explorer to rename the "received" file to JsonParserTest.ItConvertsJsonToPoco.approved.txt.

I will go with option two because I don’t trust copy/paste not to make mischief with things like line-endings and character encoding.

After renaming the file, I run the test again and it passes. I should note that I normally choose to use the composite DiffReporter which searches my system for a working diff utility and uses that to show me the results. These utilities (Kdiff3, BeyondCompare, Perforce, and many more…) usually let me approve the result without resorting to renaming files. I don’t know what Microsoft thinks it is accomplishing by hobbling their diff utility in this way.

Next, I delete the original assert, re-run the test, and it passes.

/// <summary>
/// Parse this JSON into a POCO object.
/// </summary>
[TestMethod]
[UseReporter(typeof(VisualStudioReporter))]
public void ItConvertsJsonToPoco()
{
    var text = File.ReadAllText(PathUtilities.GetAdjacentFile("sample.json"));
    var o = Event.DeserializeJson(text);
    Approvals.Verify(o);
}

Now that the original Assert is gone, my refactoring tool tells me that the Expected field (formerly Answer) is unused, so I delete it, and run the test.

With the second giant string removed, I’m left with this:

namespace ApprovalsExample
{
    using System.IO;
    using ApprovalTests;
    using ApprovalTests.Reporters;
    using ApprovalUtilities.Utilities;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    /// <summary>
    /// Describe a JSON parser.
    /// </summary>
    [TestClass]
    public class JsonParserTest
    {
        /// <summary>
        /// Parse this JSON into a POCO object.
        /// </summary>
        [TestMethod]
        [UseReporter(typeof(VisualStudioReporter))]
        public void ItConvertsJsonToPoco()
        {
            var text = File.ReadAllText(PathUtilities.GetAdjacentFile("sample.json"));
            var o = Event.DeserializeJson(text);
            Approvals.Verify(o);
        }
    }
}

And I’ve reached my goal. If you still care about signal-to-noise ratio, its 2:3. But more importantly, the entire test, including all the kruft of namespaces, attributes and comments can be seen and understood at a glance. I would probably not spend more than a few seconds reading this test before moving on to read the actual implementation of DeserializeJson. ApprovalTests has allowed me to shorten up this test, which makes the test take up less mental real-estate, and allows me to use more of my brain thinking about the production code instead of the test.

The code for this example is available on GitHub.

What’s New in CompositionTests 2.0

Download the latest version of CompositionTests from nuget.org!

ApprovalTests 3.0

Updated the dependency on ApprovalTests to 3.0.01. Thanks to the new version updating policy for ApprovalTests, CompositionTests should remain forward compatible with future versions of ApprovalTests, unless there are breaking changes in the API.

New version policy

Following LLewellyn’s lead with ApprovalTests, I am adopting a JSON.NET-style version update policy. Adopting this policy will enable me to sign CompositionTests in the future without creating forward-compatibility problems for anyone else. For now, the package remains unsigned because its other dependency, the MEFX Diagnostic Library is unsigned. I’ll have to decide if I’m willing to do anything about that before I can consider a signed version of CompositionTests.

The impact is that the CompositionTests AssemblyVersion will stay at 2.0.0 from now on. The real version can be found by looking at AssemblyFileVersion, or by looking at the nuget package version, which will be 2.0.1 for this release.

Common Language Specification Compliance

The CompositionTests library now declares itself CLS compliant. However, MEFX.Core does not make the same declaration, so certain methods that interact with the core are individually marked non-compliant. I don’t think that MEFX.Core uses anything non-compliant, the library is simply missing the declaration of compliance. I don’t think Microsoft has plans to provide any more updates to this piece, so I’ll have to decide that I’m willing to modify and maintain a fork of MEFX.Core before I can do anything about that missing attribute.

Removed Obsolete Methods

Methods and types marked with the ObsoleteAttribute in the 1.0 time-frame have been removed in order to clean up the interface in 2.0. You must now migrate to Verify* and MefComposition if you wish to use new versions of the library.

Beyond the Event Horizon: WinForms Event System

OLYMPUS DIGITAL CAMERAYou remember WinForms don’t you? WinForms applications are still around, in huge quantities. Some of us still need to maintain or rehabilitate WinForms applications, and testing events is particularly important when working with WinForms. The WinForms designer takes responsibility for wiring up many events for you, then discourages you from thinking too hard about what it did. Not only does it put the event wiring code in a semi-hidden Designer file, it also encloses the code it doesn’t want you touch in a region and adds a large comment explaining that you really should slowly back away before you hurt yourself.

Even if you heed all of the warnings and stay out of that code, its still far too easy to add useless empty handlers from within the designer, or worse—accidentally unhook your events.  Unit testing your handler implementations wont help, nothing in the internal implementation changes when a handler is wired (or unwired) from an event.  You need something like the event tests I’ve been working on to make sure that the proper code is invoked when you perform an action on the GUI (for example, clicking a button).

To build a system for inventorying events on WinForms controls, you should have a good idea how events work elsewhere in the .NET Framework.  Here’s a short list of posts on this blog which should get you up to speed:

  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.
  4. Beyond the Event Horizon: Event Complications” — Completes the toolset introduced in part 3 by handling inherited events and events of any delegate type.

I’ve made the code associated with these articles available on GitHub.  The code described in this article is a reimplementation of features available in a free, open source testing library called ApprovalTests, which you can download from SourceForge or NuGet and start using immediately.  By using ApprovalTests, you can save yourself the bother of cut-and-paste, and the maintenance headache of keeping your own copy of the code.  Need help getting started with ApprovalTests?  Check out Llewellyn Falco’s fantastic series on YouTube and you will be up to speed in no time.

So, will my event testing system work on WinForms? On the surface it seems like it will, but lets give it a try and see what happens.

Start With A Failing Test

First I’ll create a simple Form.  I won’t be using the designer.  Some of you might have never ventured into the “forbidden zone” inside the Designer file.  I’ll implement the Form by hand, and this will give you your first bit of insight into how events work in WinForms.

First I need to add a reference to System.Windows.Forms and import the namespace having the same name.

using System.Windows.Forms;

public class DemoForm : Form
{
}

While I design, I’ll use WinFormsApprovals.Verify to get feedback as I work.

[TestClass]
public class WinFormsDemo
{
    [TestMethod]
    public void VerifyDemoFormView()
    {
        WinFormsApprovals.Verify(new DemoForm());
    }
}

This test will continue to fail until I approve it, so each time I run it I’ll see what DemoForm looks like.  Now that I can see what I’m doing I can quickly add a few controls to the form. For example, here I add a Button.

private Button button1;

public DemoForm()
{
    this.button1 = new Button();
    this.button1.Text = "Click Me!";
    this.Controls.Add(this.button1);
}

And I can see what this looks like in TortoiseIDiff:

WinFormsEvents

And here’s a CheckBox:

private CheckBox checkBox1;

public DemoForm()
{
    this.button1 = new Button();
    this.button1.Text = "Click Me!";
    this.Controls.Add(this.button1);

    this.checkBox1 = new CheckBox();
    this.checkBox1.Text = "Check Me!";
    this.checkBox1.Location = new Point(
        this.button1.Location.X + this.button1.Width + 10,
        this.button1.Location.Y);
    this.Controls.Add(this.checkBox1);
}

The layout code is ugly, but it gets the job done.

WinFormsEvents2

Finally, I’ll have a ListBox too:

public DemoForm()
{
    this.button1 = new Button();
    this.button1.Text = "Click Me!";
    this.Controls.Add(this.button1);

    this.checkBox1 = new CheckBox();
    this.checkBox1.Text = "Check Me!";
    this.checkBox1.Location = new Point(
        this.button1.Location.X + this.button1.Width + 10,
        this.button1.Location.Y);
    this.Controls.Add(this.checkBox1);

    this.listBox1 = new ListBox();
    this.listBox1.Location = new Point(
        10,
        this.button1.Location.Y + this.button1.Height + 10);
    this.listBox1.Size = new Size(
        this.Width - 40,
        this.Height - this.button1.Height - 40);
    this.Controls.Add(this.listBox1);
}

And here’s my final Form.  Fantastic design if you ask me.  I’ll apply a ClipboardReporterAttribute to the test method and approve the result.  Doing this locks down the look and feel.

WinFormsEvents3

Now I want to add another failing test to lock down the event handlers for the form.

[TestMethod]
public void VerifyDemoFormEvents()
{
    EventUtility.VerifyEventCallbacks(new DemoForm());
}

And here are the results:

Event callbacks for DemoForm

Throughout these posts I’ve been implying that this code base wont work with WinForms but we haven’t proved that yet.  Before I can say that its not working, I need to wire up some events!

public DemoForm()
{
    // ...

    this.button1.Click += this.ButtonClick;
    this.checkBox1.CheckedChanged += this.HandleCheckedChanged;
    this.Load += this.HandleFormLoad;
}

I’ve wired up three events but none of them show up in the results.  Right away I can see that there might be a problem with the Button and CheckBox, those events are wired up to child objects, and I’ve never tried to do anything other than get events off the top level object.  However, the HandleFormLoad method is wired up to the Form.Load event, which is part of the top-level object and isn’t showing up either.

Now I’ve got a TODO list:

  1. Figure out why the handler attached to Load is missing.
  2. Detect events wired up to child controls.

WinForms Event Implementation

In my last post, I thought I had discovered the end-all-be-all technique for finding events of any type, but the missing Load event proves me wrong.  According to MSDN, Form.Load has this signature:

public event EventHandler Load

That doesn’t seem very exotic.  When I debug my test and query the type in the Immediate window, I get the following output:

typeof(DemoForm).GetEvents()
{System.Reflection.EventInfo[91]}
    [0]: {System.EventHandler AutoSizeChanged}
    [1]: {System.EventHandler AutoValidateChanged}
    [2]: {System.ComponentModel.CancelEventHandler HelpButtonClicked}
    [3]: {System.EventHandler MaximizedBoundsChanged}
    [4]: {System.EventHandler MaximumSizeChanged}
    [5]: {System.EventHandler MarginChanged}
    [6]: {System.EventHandler MinimumSizeChanged}
    [7]: {System.EventHandler TabIndexChanged}
    [8]: {System.EventHandler TabStopChanged}
    [9]: {System.EventHandler Activated}
    [10]: {System.ComponentModel.CancelEventHandler Closing}
    [11]: {System.EventHandler Closed}
    [12]: {System.EventHandler Deactivate}
    [13]: {System.Windows.Forms.FormClosingEventHandler FormClosing}
    [14]: {System.Windows.Forms.FormClosedEventHandler FormClosed}
    [15]: {System.EventHandler Load}
    // Many more events...

Maybe something is wrong with the way I’m building my type collection in GetEventCallbacks. I’ll use Extract Method to break out the type collection query so that I can test it in isolation.

public static IEnumerable<EventCallback> GetEventCallbacks(
    this object value)
{
    return value.GetEventsForTypes(GetEventTypes(value).ToArray());
}

public static IEnumerable<Type> GetEventTypes(object value)
{
    return GetType(value).GetEvents().Select(ei => ei.EventHandlerType).Distinct();
}

This change doesn’t break any existing tests.  I’ll add a test to see what happens when this method queries DemoForm.

[TestMethod]
public void GetEventTypeForDemoForm()
{
    ApprovalTests.Approvals.VerifyAll(
        ReflectionUtility.GetEventTypes(new DemoForm()), string.Empty);
}

The results look and EventHandler, the delegate type backing Form.Load, is right there in slot 0.

[0] = System.EventHandler
[1] = System.ComponentModel.CancelEventHandler
[2] = System.Windows.Forms.FormClosingEventHandler
[3] = System.Windows.Forms.FormClosedEventHandler
[4] = System.Windows.Forms.InputLanguageChangedEventHandler
[5] = System.Windows.Forms.InputLanguageChangingEventHandler
[6] = System.Windows.Forms.ScrollEventHandler
[7] = System.Windows.Forms.ControlEventHandler
[8] = System.Windows.Forms.DragEventHandler
[9] = System.Windows.Forms.GiveFeedbackEventHandler
[10] = System.Windows.Forms.HelpEventHandler
[11] = System.Windows.Forms.InvalidateEventHandler
[12] = System.Windows.Forms.PaintEventHandler
[13] = System.Windows.Forms.QueryContinueDragEventHandler
[14] = System.Windows.Forms.QueryAccessibilityHelpEventHandler
[15] = System.Windows.Forms.KeyEventHandler
[16] = System.Windows.Forms.KeyPressEventHandler
[17] = System.Windows.Forms.LayoutEventHandler
[18] = System.Windows.Forms.MouseEventHandler
[19] = System.Windows.Forms.PreviewKeyDownEventHandler
[20] = System.Windows.Forms.UICuesEventHandler

The next logical problem could be that the GetEventsForTypes method is not finding any fields assignable to EventHandler on Form or any of its ancestors.  I’ll write another test to focus in on that possibility.

[TestMethod]
public void GetEventsForDemoFormEventHandlers()
{
    ApprovalTests.Approvals.VerifyAll(
        new DemoForm().GetEventsForTypes(typeof(EventHandler)),
        string.Empty);
}

ApprovalTests reports that the result set is empty.  That’s a problem.  Why isn’t it finding the field backing Form.LoadIs there a field backing Form.Load?  My testing assumes that every event is implemented by the compiler, and as a result, every event should have a delegate backing the event, declared as a private field.  What if WinForms uses custom add/remove methods instead of compiler implemented events?

I can use ILSpy to figure out what’s going on.  Sure enough, ILSpy shows a custom add/remove implementation.

public event EventHandler Load
{
    add
    {
        base.Events.AddHandler(Form.EVENT_LOAD, value);
    }
    remove
    {
        base.Events.RemoveHandler(Form.EVENT_LOAD, value);
    }
}

Instead of adding and removing delegates from a private instance field on Form, these add and remove methods make calls to a protected property called Events. ILSpy tells me that Events is an instance of EventHandlerList. I don’t know what that is yet, but before I try to figure it out I want to spend a little more time inside Form. The purpose of value is easy to understand, it is the delegate to add or remove, but what about Form.EVENT_LOAD?

EVENT_LOAD refers to a private static read-only object, initialized to new object(). This argument it is just a reference to some unique chunk of memory on the managed heap, it can’t be changed, and every instance of Form has access to the same unique reference. I notice that there are many more static objects like this on Form. For example, Form has EVENT_MENUCOMPLETE, EVENT_MENUSTART, EVENT_RESIZEBEGIN, and so on. Presumably there is a static object which corresponds to each event implemented with custom add/remove methods similar to Form.Load.

If I navigate to the Events property declaration I find it declared on System.ComponentModel.Component:

protected EventHandlerList Events
{
    get
    {
        if (this.events == null)
        {
            this.events = new EventHandlerList(this);
        }
        return this.events;
    }
}

The property lazily instantiates an EventHandlerList when needed, and that’s about it. EventHandlerList also lives in the System.ComponentModel namespace. Despite it’s name, it does not derive from List, nor does it implement any list or collection interfaces.  I’ll look at AddHandler to see what it does when it receives the static object and handler delegate.

public void AddHandler(object key, Delegate value)
{
    EventHandlerList.ListEntry listEntry = this.Find(key);
    if (listEntry != null)
    {
        listEntry.handler = Delegate.Combine(listEntry.handler, value);
        return;
    }
    this.head = new EventHandlerList.ListEntry(key, value, this.head);
}

From this method’s point of view, the static object is called key and the delegate is called value. So, EventHandlerList is logically closer to a dictionary than a list.  When AddHandler executes, the dictionary attempts to find an existing value with that key. When found, the new delegate is combined with the existing delegates, otherwise the method creates a new dictionary entry. The last line of the method might give some insight into how EventHandlerList stores its data, if I knew how EventHandlerList.ListEntry was implemented.

Here is ListEntry‘s entire implementation:

private sealed class ListEntry
{
    internal EventHandlerList.ListEntry next;
    internal object key;
    internal Delegate handler;
    public ListEntry(object key, Delegate handler, EventHandlerList.ListEntry next)
    {
        this.next = next;
        this.key = key;
        this.handler = handler;
    }
}

In a typical .NET dictionary, each entry provides a key and a value. ListEntry provides these members, but also a reference to the next entry. So, EventHandlerList is a hybrid dictionary/linked-list.

Missing Pieces

After finding ListEntry I don’t need to look any further. Since WinForms has taken us deep into a rabbit hole, I’ll restate my goals:

Given a Form to examine, make one call that will inventory the event handlers attached to the Form and each of the Form’s controls. Display the inventory where each invocation list is associated with the event it’s attached to, and each event is associated with the type it is declared on.

Let’s compare how I solved each part of this problem in the Poco example with how I will need to solve it in the WinForms example.

  • Find Invocation List
    • Poco: Find the delegate field backing the event; call GetInvocationList
    • WinForms: Find the ListEntry instance in Events; access the handler field and call GetInvocationList
  • Associate Invocation List with Event
    • Poco: Use the delegate field name.
    • WinForms: Use the name of the static object used as the ListEntry key.
  • Associate Event with Object
    • Poco: Use the Type name.
    • WinForms: Use the Form type name for Form Events.  Use the Control type name for each child control.

I think the biggest difference while constructing a query will be the way the invocation list is associated with an event name.  With POCO events I could get both the invocation list and the name from the same delegate reference.  By the time a Component stores an event delegate in a ListEntry the event name is lost and everything is called key. The CLR doesn’t care about the name because it checks the key with a reference comparison, it just needs the pointer.

Besides the query, I’m going to need a lot of plumbing just to make the data structure queryable. ListEntry is private, nested and sealed. The compiler wont even acknowledge it’s existence:

EventHandlerList.ListEntry entry;  // Won't compile

Error:

Error   1   The type name 'ListEntry' does not exist in the type 'System.ComponentModel.EventHandlerList'

And since EventHandlerList doesn’t implement IEnumerable or IEnumerable<T>, it doesn’t play nice with LINQ:

EventHandlerList ehl = new EventHandlerList();
var q = from e in ehl select e; // Wont compile

Error:

Error   1   Could not find an implementation of the query pattern for source type 'System.ComponentModel.EventHandlerList'.  'Select' not found.

So, before I can write my queries, I’ll need to get under there and do some plumbing.

Relationship With EventApprovals

This article has been a bit of a tease.  While I hope I’ve provided some useful information, I haven’t really produced any solutions.  The relationship with EventApprovals is that all of these problems are already solved in ApprovalTests!  When you call ApprovalTests.Events.EventApprovals.VerifyEvents, WinForms events are supported and will appear in your inventory.

Up Next

In this article’s next installment: “Beyond the Event Horizon: WinForms Plumbing”, I will use what I’ve learned about WinForms to build up the plumbing pieces necessary to make a usable wrapper for ListEntry and an enumerable adapter for EventHandlerList.  With those pieces of plumbing out of the way then I can return to my TODO list, find the missing Form.Load event, and figure out how I want to capture the events declared on child controls.

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

Beyond the Event Horizon: Events You Don’t Own

It’s easy to retrieve an invocation list when you have a delegate instance in hand.  When it comes to events, its easy to get your hands on the backing delegate instance when you own the class wherePA310020 the event is declared.

However, there is a whole universe of events that you don’t own.   These events are declared on BCL classes, third party components, or on that one God class that your boss won’t let you change.  Or maybe you’re picky about encapsulation and want to get your hand on the delegate without adapting your production code to the needs of your tests.  If you find yourself in any of these situations, .NET reflection provides everything you need to get your hands on the delegate and write tests.  This article will show you how to extract the delegates backing POCO events, and in later posts I’ll cover more difficult scenarios—including the WinForms event system.

“What’s a POCO event?” you ask.  POCO events are just my term for the simplest possible event you could write.  Find out more about what the compiler does when you declare an event by visiting the previous posts in this article.  In Part One: “Beyond the Event Horizon: Delegate Basics” I introduced some relevant aspects of .NET delegates.  In part Two: “Beyond the Event Horizon: Event Basics” I defined POCO events and covered their implementation.

In a roundabout way, this series of posts is about ApprovalTests.  If you haven’t heard of ApprovalTests, it’s a great open source library which you can use to enhance your tests.  I gave a 10,000 foot overview of ApprovalTests in part one of this series, but if you want to get the most out of reading this article, check out Llewellyn Falco’s series on YouTube.

If you are really interested in the nitty-gritty and playing around with the ideas in this article, check out the accompanying  code repository on GitHub.  Also remember that the codebase I’m building in these articles is  a reimplementation of features already available in ApprovalTests 2.0.  If you want to use these features, don’t waste your time with cut-and-paste, go grab ApprovalTests.

With all that intro out of the way, lets talk about reflection.

Testing Events You Don’t Own

In the previous article, I created a class called Poco. This simple class declares an event that signals when Poco completes it’s (imaginary) work.  As the example progressed, I started to make some assumptions about Poco’s behavior and add restrictions to what I could change about Poco’s implementation.  For this example, I’ll keep all the previous assumptions:

  • Poco is part of a primitive layer, meant to be accessed through a higher-level API.
  • Poco’s main activity is performed by DoWork, which is too expensive/inconvenient to call.
  • PocoClient is part of the high-level API and must listen to the Poco.ProcessCompleted event.
  • I don’t control Poco, so simply changing it’s implementation is not an option.

For a simple class like Poco it’s not hard to retrieve the handler using reflection.  First I’ll get rid of the GetProcessCompletedHandler method I added to Poco in the previous example. I’ll implement it as an extension method instead and make no changes to the test.  The extension method isn’t strictly necessary but it will keep the reflection code from cluttering up the test.

I’ll create a static class to host the extension method.

using System;
using System.Linq;
using System.Reflection;

public static class ReflectionUtility
{
    public const BindingFlags NonPublicInstance = BindingFlags.Instance | BindingFlags.NonPublic;

    public static EventHandler GetProcessCompletedHandler(this Poco poco)
    {
        var matchingFields = from fieldInfo in poco.GetType().GetFields(NonPublicInstance)
                             where typeof(EventHandler).IsAssignableFrom(fieldInfo.FieldType)
                             && fieldInfo.Name == "ProcessCompleted"
                             select (EventHandler)fieldInfo.GetValue(poco);
        return matchingFields.Single();
    }
}

This extension method allows the GetPocoEventInvocationList test to pass without altering Poco.  I’ll add a test for PocoClient now, which should verify the scenario that got us here, then spend some time examining how GetProcessCompletedHandler works.

[TestMethod]
public void PocoClientListensToPoco()
{
    Poco poco = new Poco();
    PocoClient client = new PocoClient(poco);
    DelegateUtility.VerifyInvocationList(poco.GetProcessCompletedHandler());
}

The results confirm that PocoClient is doing its job:

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

Reflection Anatomy

Let’s get into GetProcessCompletedHandler guts.  My goal is to get my hands on the event’s backing field.  I know from using Reflector that the field is a private instance field, and although it is invisible, I know the name the compiler will choose for the field—it’s the same name I gave to the simple event declaration.

I can use reflection to get any information I want off the type, as long as I know how its declared and what its name is.  By default, reflection methods supply public instance members.  If you want to include the private members, you have to be specific about the BindingFlags you pass in.  So I create a const in ReflectionUtility to specify the non-public (i.e., private, protected, internal) and instance flags.  My primary reason for giving these flags a name is to increase readability, but as ReflectionUtility grows, this const will also save me a lot of typing.

public const BindingFlags NonPublicInstance = BindingFlags.Instance | BindingFlags.NonPublic;

With that out of the way, I can request information about the poco instance’s private fields.  First, I need to get a Type object which corresponds to Poco (the class) which I can obtain by calling GetType on poco (the object).  The Type object describes the Poco class, and I use GetFields with my binding flags to get the collection of descriptors for the Type’s private fields.

from fieldInfo in poco.GetType().GetFields(NonPublicInstance)

Next, I filter for EventHandlers:

where typeof(EventHandler).IsAssignableFrom(fieldInfo.FieldType)

Then I filter by name:

&& fieldInfo.Name == "ProcessCompleted"

FieldInfo instances describe fields, but I want the actual field value from the poco instance.  To retrieve the actual instance value, I use the GetValue method, which returns an object reference to the actual value from poco.  I don’t want an object, I want an EventHandler.  I can safely make that cast because my first filter ensures that this object is castable to EventHandler.  This is what I end up selecting:

select (EventHandler)fieldInfo.GetValue(poco);

Because there can only be one field named “ProcessCompleted”, the name filter ensures that there is only one member in the collection returned by this query.  So, I pull that member out of the collection using Single:

return matchingFields.Single();

And that, it would seem, is all you need to know about retrieving the delegate field backing an event..

Making it Better

The best part about having green tests is that you can stop thinking so hard and just play.  GetProcessCompletedHandler is OK, but it can be improved.  I’ll start by fixing the most obvious defect, the method doesn’t check its input.  What happens when I throw a null in there?

[TestMethod]
public void NullHasNoProcessCompletedHandler()
{
    Assert.IsNull(ReflectionUtility.GetProcessCompletedHandler(null));
}

Not surprisingly, this test throws a NullReferenceException before even reaching the assertion.  The exception occurs when the query calls GetType on the null value.   This check should fix the problem for now.

public static EventHandler GetProcessCompletedHandler(this Poco poco)
{
    if (poco == null)
    {
        return null;
    }

Now that the most obvious problem is out of the way, my biggest complaint is the magic string “ProcessCompleted”.  If events were (even) more like properties, I could use an expression tree to specify the member with a lambda.  Alas events are not properties, and they don’t work well (at all?) with expression trees.  Perhaps I can make the magic string palatable by promoting it to a parameter.

public static EventHandler GetProcessCompletedHandler(this Poco poco, string eventName)
{
    if (poco == null)
    {
        return null;
    }

    var matchingFields = from fieldInfo in poco.GetType().GetFields(NonPublicInstance)
                         where typeof(EventHandler).IsAssignableFrom(fieldInfo.FieldType)
                         && fieldInfo.Name == eventName
                         select (EventHandler)fieldInfo.GetValue(poco);
    return matchingFields.Single();
}

I used my refactoring tool to introduce the new parameter, and it automatically updated the test call sites to look something like this:

[TestMethod]
public void PocoClientListensToPoco()
{
    Poco poco = new Poco();
    PocoClient client = new PocoClient(poco);
    DelegateUtility.VerifyInvocationList(poco.GetProcessCompletedHandler("ProcessCompleted"));
}

That seems like a small improvement but I’m not really satisfied with it.  I’ll file away the magic string for a moment, because now I notice something else about the extension method.  The only Poco-specific piece of data was the event name, and now that I have moved the magic string, this method should now work as an extension on object.  After making that change I also rename the parameter to “value”, since the instance is not necessarily a Poco anymore.

public static EventHandler GetProcessCompletedHandler(this object value, string eventName)

My test still pass, so this change is all good.  Now I notice that the method name still has “ProcessCompleted” in it.  That name no longer reflects what the method does, so I rename to “GetEventHandler'” with my refactoring tool.

public static EventHandler GetEventHandler(this object value, string eventName)

My test still pass.  Now I’m wondering if I can make it even more generic.  I’m still not happy with my magic string either.  Instead of one magic string in one extension method, I now have a magic string in every test.  So, promoting the string to a parameter made the extension method look nicer, but at the expense of making everything else uglier.

The other problem with this method is that I can only retrieve the delegate for one event at a time.  Poco is simple, it has one event handler.  PocoClient has a simple relationship with Poco. What if both objects were more complex? Poco might have many handlers, and PocoClient might subscribe to some subset of those handlers. Querying event handlers 1 by 1 would be tedious in a scenario like that.  I would need to know their names (and spell them correctly), and each would require its own test. If I introduced new events to Poco, I’d have to remember to add a test each time.  Chances are good that sometime down the line an event will be missed, and end up uncovered.

I can eliminate both maintenance problems in one (big) step, just by getting rid of the name filter.  Instead of retrieving just one event delegate, I could get the inventory of all the EventHandler delegates on Poco.  When Poco gets new events, they will show up in the test as soon as they’re introduced, without writing any new test code.  Instead of using the Name property as a filter, I could include it as part of the return value.  However, without the filter on the field name, I can’t use Single to pull the result from the collection, because there might be more than one field with handlers attached.  So I’ll have to change the return type as well.  Here is what my method looks like after making these changes.

public static IEnumerable<Tuple<string, EventHandler>> GetEventHandler(this object value)
{
    if (value == null)
    {
        return null;
    }

    return from fieldInfo in value.GetType().GetFields(NonPublicInstance)
           where typeof(EventHandler).IsAssignableFrom(fieldInfo.FieldType)
           select new Tuple<string, EventHandler>(fieldInfo.Name, (EventHandler)fieldInfo.GetValue(value));
}

This breaks the build, since my tests are expecting a single EventHandler, not a collection of Tuples.  I could go try to fix the tests at this point, but how can I?  The tests all use VerifyInvocationList, which also expects a single Delegate.   It looks like I’m going to end up either refactoring my Verify method or creating another Verify method to handle a Tuple collection.  Also this line is starting to bother me, its too long and it has too much punctuation.

select new Tuple<string, EventHandler>(fieldInfo.Name, (EventHandler)fieldInfo.GetValue(value));

Looking at this line, I want to create a data transfer class to hold onto my two pieces of data, getting rid of the Tuple.  If I follow that urge, then I should do it now, before updating the tests, otherwise I’ll end up changing the tests twice.

The DTO doesn’t need to be complicated, this should suffice for now.

public class EventCallback
{
    public EventCallback(string name, Delegate callback)
    {
        this.Name = name;
        this.Callback = callback;
    }

    public Delegate Callback { get; set; }
    public string Name { get; set; }
}

After this modification, the projection is cleaner, but I think it still suffers from an excess of parens.

select new EventCallback(fieldInfo.Name, (EventHandler)fieldInfo.GetValue(value));

I’ll create an extension method that lets me replace some of those parens with angles.

public static T GetValue<T>(this FieldInfo fi, object value)
{
    return (T)fi.GetValue(value);
}

Now I can rewrite my projection like this.

select new EventCallback(fieldInfo.Name, fieldInfo.GetValue<EventHandler>(value));

Ok that was fun, but my build is still broken.  I’ll give the tests some love next, but I need to make one more update to this extension method.  I’ll change its name to GetEventHandlers to reflect that it returns a collection.

As mentioned above, the most immediate problem with the tests are that they are not expecting collections.  In some tests, I resolve this issue by switching to the var keyword, and in others I need to introduce a local variable to hold onto the new collection.  But, as expected, the build is still broken because the Verify method wont take a collection.  I’ll stop using my custom verifier and go back to ApprovalTest basics.  Here is a new version of GetPocoEventInvocationList.

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

    var pocoDelegates = poco.GetEventHandlers();
    Approvals.VerifyAll(pocoDelegates, string.Empty);
}

II have to comment out a line in a broken test so that I can run this and I don’t like the result, it doesn’t tell me any useful information.

[0] = EventReflection.Demo.EventCallback

I can pass a formatter to VerifyAll, or I can override EventCallback.ToString to provide a nicely formatted string representation.  The ability to override ToString is an advantage I gained when I decided to use a custom DTO, that I wouldn’t have if I had stuck with the Tuple.  Eventually, I will override ToString, but for now I’ll just use a formatter until I get the formatting I want, then I’ll move that formatter into EventCallback.

Here are the results from my first attempt:

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

And the code which produces it:

Approvals.VerifyAll(
    pocoDelegates, 
    e => "{0} => {1}".FormatWith(e.Name, e.Callback.GetInvocationList().Select(d => d.Method).ToReadableString()));

This is an improvement, but both the result and the code are a little complicated for my taste.  I liked the vertical spacing that VerifyInvocationList provided more than the horizontal formatting ToReadableString provides.  The formatter is too long, an it repeats code already in VerifyInvocationList.

Second attempt looks much nicer.  It takes better advantage of vertical space and retains the index numbers for each method in the invocation list.  This is actually important information, since the delegates will be executed in the order that they appear in the invocation list:

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

Here is the formatter which produces this output.

e =>
{
    var buffer = new StringBuilder();
    buffer.AppendLine(e.Name);
    var delegates = e.Callback.GetInvocationList();
    for (int index = 0; index < delegates.Length; index++)
    {
        var d = delegates[index];
        buffer.AppendLine("\t[{0}] {1}".FormatWith(index, d.Method));
    }
    return buffer.ToString();
}

I use a buffer to hold onto the data as I build the results.  It’s pretty straightforward, although you might not be familiar with the extension method FormatWith.  This extension ships with the ApprovalUtilities library that accompanies ApprovalTests, and I find it’s a cleaner way to call string.Format.  I like the new formatting, so I’ll make this formatter into the ToString implementation for EventCallback.  Then I can simplify my test, I keep a simple formatter, to prevent VerifyAll from prefixing additional index numbers to each result.

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

    var pocoDelegates = poco.GetEventHandlers();
    Approvals.VerifyAll(pocoDelegates, e => e.ToString());
}

And I can update my other failing test as well:

[TestMethod]
public void PocoClientListensToPoco()
{
    Poco poco = new Poco();
    PocoClient client = new PocoClient(poco);
    var eventHandlers = poco.GetEventHandlers();
    Approvals.VerifyAll(eventHandlers, e => e.ToString());
}

Of course, now I have duplication, and I have a weird little formatter there, which I’ll need to remember if I want to get these results every time.  So I should encapsulate that duplication so I don’t need to remember it anymore.

using System.Collections.Generic;
using ApprovalTests;

public static class EventUtility
{
    public static void VerifyEventCallbacks(IEnumerable<EventCallback> callbacks)
    {
        Approvals.VerifyAll(callbacks, c => c.ToString());
    }
}

And I can update my tests.

[TestMethod]
public void PocoClientListensToPoco()
{
    Poco poco = new Poco();
    PocoClient client = new PocoClient(poco);
    EventUtility.VerifyEventCallbacks(poco.GetEventHandlers());
}

That was a bit of work to get rid of the magic string.  However, it should be worth it because the new query will also detect new event handlers when they’re added.  My current tests don’t verify this behavior.  Also, I had some tests against VerifyInvocationList that showed multiple handlers wired up to one delegate, but that scenario isn’t covered yet with VerifyEventCallbacks.

To get these scenarios covered, I’ll add another event to Poco.  I’m still assuming that I don’t control Poco, but we just transitioned into an alternate reality where Poco always had more than one event.  I’ll call the new event ProcessStarted, so here’s what we’ve got now.

public event EventHandler ProcessCompleted;
public event EventHandler ProcessStarted;

Simply adding this event declaration is enough to break two of my tests.  My first thought is that it is an approval failure because the new event is already detected even though I hadn’t wired anything up yet.  However, it turns out that EventCallback.ToString is throwing a null reference exception.  Of course, this is because the backing field for an event will be null until the first event is wired up.  The event was detected, but since it was null, the Callback property was also null.  Because the delegate is null, I get a null reference exception on GetInvocationList.

After considering my options, I decide to filter out the nulls in the query.  I don’t want to list any events that aren’t wired up anyway, they would just be noise.  To avoid reflecting into my object twice, I’ll introduce a new variable in the query using the let keyword, then filter out nulls.

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);

This fixes my existing tests, but I still need to cover my missing scenarios.

[TestMethod]
public void MultipleEvents()
{
    var poco = new Poco();
    poco.ProcessCompleted += Domain.HandleProcessCompleted;
    poco.ProcessStarted += Domain.HandleProcessStarted;
    EventUtility.VerifyEventCallbacks(poco.GetEventHandlers());
}

This test produces these results, which look good so I approve them

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

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

And one more test to cover the multicast scenario.

[TestMethod]
public void MulticastEvent()
{
    var poco = new Poco();
    poco.ProcessCompleted += Domain.HandleProcessCompleted;
    poco.ProcessCompleted += Domain.HandleProcessStarted;
    EventUtility.VerifyEventCallbacks(poco.GetEventHandlers());
}

And this test produces good results too.

ProcessCompleted
    [0] Void HandleProcessCompleted(System.Object, System.EventArgs)
    [1] Void HandleProcessStarted(System.Object, System.EventArgs)

I’m pretty happy with that result. But I still have a problem, I have to look at the test to figure out which object these event handlers are attached to. The result could be improved if they included some description where they came from. This shouldn’t be too hard to fix.

First I’ll change the VerifyEventCallbacks implementation to take the object itself, rather than the collection of callbacks.  Once I have the object I can query it for the type name, then query it for the callbacks, then collect it all into a buffer.

public static void VerifyEventCallbacks(object value)
{
    var buffer = new StringBuilder();
    buffer.AppendLine("Event callbacks for {0}".FormatWith(value.GetType().Name)).AppendLine();
    foreach (var callback in value.GetEventHandlers())
    {
        buffer.AppendLine(callback.ToString());
    }

    Approvals.Verify(buffer);
}

Then I need to update my tests to pass the object instead of a collection.

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

Which produces results that indicate where the events are are attached:

Event callbacks for Poco

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

That’s pretty nice.  There’s always another problem though.  The new implementation queries for the value’s type before the null check in GetEventHandlers.   It probably can’t handle nulls.  I’ll write a test to confirm and fix.

[TestMethod]
public void NullHasNoEvents()
{
    EventUtility.VerifyEventCallbacks(null);
}

As expected this generates a null reference exception.  Approvals.Verify can actually handle null just fine, so if value is null, I just need to make sure that a null gets verified.  This update to VerifyEventCallbacks does the trick.

public static void VerifyEventCallbacks(object value)
{
    StringBuilder buffer = null;
    if (value != null)
    {
        buffer = new StringBuilder();

        buffer.AppendLine("Event callbacks for {0}".FormatWith(value.GetType().Name)).AppendLine();
        foreach (var callback in value.GetEventHandlers())
        {
            buffer.AppendLine(callback.ToString());
        }
    }

    Approvals.Verify(buffer);
}

This works as expected, which is to say that when Approvals.Verify receives the null buffer, it produces an empty result file. With this last change I think I’m done for now.  There might still be ways to make this code better, but nothing it popping out at the moment.  This is a good time to pause and review.

When I create my test target and call VerifyEventCallbacks the results tell me this:

  1. The target type.
  2. Of all the possible events on the type, which had handlers attached.
  3. For each event, the handlers are listed in the order they execute.

Relationship with EventApprovals

The EventApprovals DTO is a little different than EventCallback.  Its called CallbackDescriptor and while it also includes the event’s name, it holds on to the projected MethodInfo collection  instead of holding on to the delegate.

In EventApprovals, you can get the collection of POCO event callbacks using ApprovalUtilities.Reflection.GetPocoEvents.  Normally you don’t need to access the event collection at that level.  If you just want to verify the event inventory, the closest method is ApprovalTests.Events.EventApprovals.VerifyEvents, its not exactly the same because it actually does more than shown here—it can inventory WinForm events as well.

Up Next

As I mentioned at the top, the reflection necessary to retrieve the backing delegate seems fairly simple.  In the case of Poco, it turned out to be straightforward.  But things might not be as simple as they seem.  All the events on Poco are based on EventHandler, what about other delegate types?  Furthermore, all the events on Poco are declared directly on Poco, what if we also had to find events inherited from a base class?  I probably wouldn’t bring it up if what I’ve shown so far worked in those scenarios, so in the next installment “Beyond the Event Horizon: Event Complications” I’ll see if I can overcome these obstacles.

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.

Beyond the Event Horizon: Delegate Basics

OLYMPUS DIGITAL CAMERAWith the release of ApprovalTests 2.0 we can now use EventApprovals to catalog the event handlers attached to our objects.  Using this feature of ApprovalTests, Stack Overflow questions like this one: How do I retrieve the Subscriber List for an Event at runtime? now have a real answer.

I asked that question almost 3 years ago.  After none other than Marc Gravell told me, “It usually means that you are doing something wrong (sorry, but it does),”  I worked out another way to test the code that led me to ask that question.  Although I accepted Marc’s answer on Stack Overflow, I think it is safe to say that I never accepted the answer in my heart.  Fast forward a few years, and yet again I found myself wishing that I could simply retrieve the subscriber list and verify it in a test.  But this time things were different.  I’m not afraid of reflection anymore, and now I have ApprovalTests available to process the potentially large amount of data that retrieving subscriber lists might produce.

So this time I wrote EventApprovals and got what I had wanted all along, the runtime subscriber list.  This CodeProject article by Alejandro Miralles was a great help getting started:  How to copy event handlers from one control to another at runtime.  With that jumping off point, I went through several revisions before telling Llewellyn, “I think I have something that might interest you…”

Along the way, I learned more about events and delegates than I ever thought I would need to know.  Indeed, my working title for this article was “More than you ever wanted to know about .NET events.”  Even though you can use ApprovalTests without knowing any of what follows, I felt like sharing some of the process that went into creating EventApprovals while it was still relatively fresh in my mind.  I thought it might be fun to re-implement a system similar to EventApprovals from memory.  You can find the code for this article on GitHub.  I figured I would make a lot of the same mistakes I made the first few times around.  I always learn better from an example that shows mistakes and how to correct them than I do from a clean implementation.  Hopefully you will too.

Along the way I’ll try to relate the concepts in this article to the parts of EventApprovals that can give you the same information without having to do the heavy lifting yourself.  So, lets get started with delegates in .NET.

Delegates

Delegates are at the heart of the event system in .NET.  And the “subscriber list” for an event is identical to the delegate’s invocation list.  When I have a direct reference to some delegate, this information is trivial to retrieve.  Here’s how.

Assume I have an instance of some delegate type:

Func<bool> truth = () => true;

In this example, our delegate is a Func<bool>, to determine the list of methods assigned to the delegate use the method called GetInvocationList.  This method returns an array of Delegate instances, and each Delegate has a Method property, which is a MethodInfo from the reflection namespace.  I’ll use a LINQ extension to project these Delegates into a collection of MethodInfos.

Something like this:

[TestMethod]
public void GetInvocationList()
{
    Func<bool> truth = () => true;
    IEnumerable<MethodInfo> methods = truth.GetInvocationList().Select(d => d.Method);
    Approvals.VerifyAll(methods, string.Empty);
}

Introduction to ApprovalTests

If you are already familiar with ApprovalTests, you can probably skip this section.  For those of you who are new to this testing library, read on for a brief introduction.  I also recommend that you watch this short video, where Llewellyn Falco, the creator of ApprovalTests, introduces the library.

Using ApprovalTests in .Net 01 MsTest

In the delegate example I wrote above, I use a call to Approvals.VerifyAll instead of using one of MSTest’s built in Assert methods.  VerifyAll will iterate over the MethodInfo collection and call ToString on each member.  These strings are collected into a buffer, then ApprovalTests shows me the results using whatever reporter I’ve specified with the UseReporter attribute.  In this case, I choose a DiffReporter.

The first time I run the test, ApprovalTests displays the results, then I can approve the results if they look right to me.  Once I have approved results, ApprovalTests will check the approved result against each subsequent test run result.  ApprovalTests will only alert me (with a failing test) if the test run produces different results from the approved results.

Invocation List Results

ApprovalTests reports these results after executing my first example:

[0] = Boolean <GetInvocationList>b__0()

I can see that only one method is assigned to this delegate. Its return type is Boolean, its name is <GetInvocationList>b__0 and it takes no parameters (indicated by the empty parenthesis()). The method has a strange compiler-generated name because it is anonymous. The list might be easier to read if I assign a ordinary named method to the delegate.

public class Domain
{
    public static bool AlwaysTrue()
    {
        return true;
    }
}

AlwaysTrue is a method which has a signature compatible with the type of the truth delegate instance, which means I can assign it to the delegate.

[TestMethod]
public void GetInvocationList()
{
    Func<bool> truth = Domain.AlwaysTrue;
    IEnumerable<MethodInfo> methods = truth.GetInvocationList().Select(d => d.Method);
    Approvals.VerifyAll(methods, string.Empty);
}

After assigning AlwaysTrue to truth I get the following output:

[0] = Boolean AlwaysTrue()

If I declare a Delegate instance I can use the static Delegate.Combine method to assign more than one method to a delegate:

[TestMethod]
public void GetMultiInvocationList()
{
    Func<bool> truth = Domain.AlwaysTrue;
    Func<bool> truthy = Domain.NeverFalse;
    Delegate multicast = Delegate.Combine(truth, truthy);
    IEnumerable<MethodInfo> methods = multicast.GetInvocationList().Select(d => d.Method);
    Approvals.VerifyAll(methods, string.Empty);
}

Here are the results:

[0] = Boolean AlwaysTrue()
[1] = Boolean NeverFalse()

Cleanup

Now that I have two tests which contain some code duplication, I’ll extract the duplication into a utility class called DelegateUtility for reuse.

public static class DelegateUtility
{
    public static void VerifyInvocationList(Delegate value)
    {
        Approvals.VerifyAll(
            value.GetInvocationList().Select(d => d.Method),
            string.Empty);
    }
}

After updating my tests they look like this, and they still pass:

[TestMethod]
public void GetInvocationList()
{
    Func<bool> truth = Domain.AlwaysTrue;
    DelegateUtility.VerifyInvocationList(truth);
}

[TestMethod]
public void GetMultiInvocationList()
{
    Func<bool> truth = Domain.AlwaysTrue;
    Func<bool> truthy = Domain.NeverFalse;
    DelegateUtility.VerifyInvocationList(Delegate.Combine(truth, truthy));
}

This was a trivial refactoring and only saved a few lines of code.  But its worth doing.  Its better to do it now when I only have two tests in my suite, than it is to wait until the suite is large and cumbersome to update.

Delegate Oddities

You may have heard that delegates are types.  This means that they are in the same category as classes, structs and interfaces.

What does it mean to be a “type”?  Well, lets look at some more familiar types: classes and interfaces.  A class and an interface are similar to each other in many ways, but they are not the same.  You can’t instantiate an interface, or define bodies for the members declared on an interface.  There are other differences, but this is enough to get an idea about what types are in .NET.  The language applies (and complier enforces) different rules about what you can and can’t do with each “type” of type.  So, delegates are not a special kind of class, they are not a special kind of interface.  Delegates are their own things with their own set of rules.  One of the rules is that the actual implementation is always compiler generated.

Lets look at what happens when the compiler encounters a delegate type definition like this:

public delegate int Operation(int a, int b);

The compiler generates a class derived from MulticastDelegate, which in turn derives from Delegate.  But only the complier can do this.  Just like you or I can’t create a method with a name like this: <GetInvocationList>b__0, we’re also prevented from inheriting from MulticastDelegate.  I’m bringing this up to explain a part of the VerifyInvocationList method that might be confusing.

Each generated MulticastDelegate descendant inherits a GetInvocationList method and a property called Method.  These two members are always related.  When the MulticastDelegate only has one method to invoke, the relationship looks like this:

[TestMethod]
public void UnicastDelegateHasInvocationList()
{
    Func<bool> truth = Domain.AlwaysTrue;
    Assert.AreEqual(
        truth.Method, 
        truth.GetInvocationList().Select(i => i.Method).Single());
}

As you can see, the Method property is also returned as a member of the Delegate array returned by GetInvocationList.  In the case of a logically unicast delegate, Method is the one and only array member.  In the case of a truly multicast delegate, things are a little different.

[TestMethod]
public void MulticastDelegateInvocationListContainsMethod()
{
    Func<bool> truth = Domain.AlwaysTrue;
    Func<bool> truthy = Domain.NeverFalse;
    Delegate combined = Delegate.Combine(truth, truthy);
    Assert.AreEqual(
        combined.Method,
        combined.GetInvocationList().Select(i => i.Method).Last());
}

In the case of a delegate that actually needs to multicast, the MethodInfo pointed at by the Method property is actually the last member of the invocation list.  In other words, when you call Delegate.Combine repeatedly, Method will always point at the last method you added to the delegate.

Here is some code that will never finish executing.

[TestMethod]
public void InfiniteLoop()
{
    Func<bool> truth = Domain.AlwaysTrue;
    this.Body(truth.GetInvocationList());
}

public void Body(Delegate[] delegates)
{
    foreach (var del in delegates)
    {
        this.Body(del.GetInvocationList());
    }
}

So we can’t crawl into each Delegate looking for more delegates, that leads to infinite recursion.  However, we don’t need to worry about it.  Delegate.Combine flattens invocation lists before combining them.  Here’s a test to demonstrate:

[TestMethod]
public void CombinedDelegatesAreFlattened()
{
    Func<bool> truth = Domain.AlwaysTrue;
    Func<bool> truthy = Domain.NeverFalse;
    Func<bool> answer43 = Domain.AlwaysTrue;
    Delegate combined = Delegate.Combine(truthy, answer43);
    Delegate flattened = Delegate.Combine(truth, combined);
    DelegateUtility.VerifyInvocationList(flattened);
}

And these are the results:

[0] = Boolean AlwaysTrue()
[1] = Boolean NeverFalse()
[2] = Boolean AlwaysTrue()

So, after playing with these tests we can see that the VerifyInvocationList implementaion is correct.

value.GetInvocationList().Select(d => d.Method)

First, I call GetInvocationList to get the flattened array of delegates.  Because these delegates are flattened, all the array members are logically unicast, so I can access the Method property to get the only member, rather than dereferencing through the array returned by GetInvocationList.

Relationship with EventApprovals

Like I said at the top, delegates are at the core of the .NET event system.  Likewise, the projection from a delegate into a collection of MethodInfos,(shown here as part of VerifyInvocationList) is at the heart of EventApprovals.

We didn’t extract VerifyInvocationList as a separate verification method the way I have done here.  If you have a delegate in hand, getting data from it isn’t very hard at all, and you can just use Approvals.VerifyAll instead of getting into the more specialized methods in ApprovalTests.Events or ApprovalUtilities.ReflectionUtilities.

Up Next

The next installment of this article is “Beyond the Event Horizon: Event Basics”  We’ll look at how events are implemented by the compiler and how they relate to delegates.