Rediscovering the Obvious

…stumbling in the footsteps of greatness

Testing delegates with nUnit and nMock

without comments

Recently, I’ve been trying to put real unit tests around the Presenters in our MVP pattern, and because the view fires events to notify the Presenter of behavior from the user, I’ve had a need to inject the proper subscription and firing of those events into nMock (2). A huge goal of nMock is to avoid creating mock objects as explicit implementations of interfaces, so I didn’t want to just give in and create full implementations for each view interface. The biggest miss, though, was confirming that the events had been properly subscribed to (as an expected condition) and then driving behavior into to component under test by firing that event with specific parameter values.

Being a good developer, I went searching (I won’t say Googling, since some MS evangelists might be reading my blog, and recently gave me a dirty look for saying it during his presentation), and this was the closest I could find:
http://becomingagile.blogspot.com/2007/03/raising-events-in-nmock-20.html

It worked great… almost. It relied on the delegate being derived from System.EventHandler, and it only passed “null, null” for sender and args. Not good enough, but a HUGE step in the right direction. So, planning on expanding his code, I realized that the single “unknowable” was HOW exactly to call the delegate. Everything else could be handled by making the delegate type into a generic. Luckily, we have a new language mechanism for extracting those little bits of unknowable calls, so I figured an anonymous delegate could probably get me where I wanted to go. So here’s what I did: (Full code at the bottom)

  1. Introduced a generic type parameter to both MockEvent<T> and MockEventHookup<T>, which represents the type of the delegate to be called.
  2. Introduced a generic delegate called void EventActivator<T>( T handler ), allowing the caller to specify the code to be executed to fire the event.
  3. Replaced all references to EventHandler with instances of T, and replaced the call to the event handler (In Raise) with a call to the EventActiviator I declared above

On the caller side, we end up with a new notation, but it pretty clearly expresses the intent… the ()’s next to handler would take any custom parameters for the delegate, and they would even be provided in the test’s context.

             // Configure the view’s mock
            MockEvent<EmptyEventHandlerDelegate> loadEvent
                = new MockEvent<EmptyEventHandlerDelegate>(
                    delegate(EmptyEventHandlerDelegate handler)
                    {
                        handler();
                    });

Then:

             Expect.Once
                 .On(view)
                 .EventAdd(“OnLoadView”, Is.Anything)
                 .Will(MockEvent < EmptyEventHandlerDelegate>.Hookup(loadEvent));
 And:

            loadEvent.Raise();

Here’s the full code for the evolved mock event.

using System;
using System.Collections.Generic;
using System.Text;
using NMock2;
using NMock2.Monitoring;
using System.IO;

namespace Presentation.Tests.HelperObjects
{
    /// <summary>
    /// Code by Eric Willeke — Keep my name here, please :)
    /// http://manicprogrammer.com/cs/blogs/willeke/archive/2007/10/17/testing-delegates-with-nunit-and-nmock.aspx
    /// Adapted from original code by Neil Bourgeois – Edmonton, Alberta, Canada
    /// http://becomingagile.blogspot.com/2007/03/raising-events-in-nmock-20.html
    /// </summary>
    public class MockEvent<T>
        where T : class
    {
        private T handler;
        EventActivator<T> activatingDelegate;

        public MockEvent(EventActivator<T> activatingDelegate)
        {
            if ( activatingDelegate == null )
                throw new ArgumentNullException();

            this.activatingDelegate = activatingDelegate;
        }

        public void Initialize(T handler)
        {
            if ( handler == null )
            {
                throw new ArgumentNullException();
            }

            this.handler = handler;
        }

        public void Raise()
        {
            activatingDelegate(handler);
        }

        public static IAction Hookup(MockEvent<T> mockEvent)
        {
            return new MockEventHookup<T>(mockEvent);
        }
    }

    public delegate void EventActivator<T>(T handler);

    public class MockEventHookup<T>
        : IAction
        where T : class
    {
        private readonly MockEvent<T> mockEvent;

        public MockEventHookup(MockEvent<T> mockEvent)
        {
            this.mockEvent = mockEvent;
        }

        public void Invoke(Invocation invocation)
        {
            T handler = invocation.Parameters[0] as T;

            if ( handler == null )
            {
                throw new Exception(
                    string.Format(
                        “Unknown event handler type. {0} was expected.”,
                        typeof(T).ToString()));
            }

            mockEvent.Initialize(handler);
        }

        public void DescribeTo(TextWriter writer)
        {
            // do nothing
        }

    }
}

Written by erwilleke

October 17th, 2007 at 9:54 am

Posted in Uncategorized

Leave a Reply