Event Sequence Unit Testing – Part 4
A lot of the development we’ve been doing here at GojiSoft has involved event driven component development. This four part article describes how we went about developing a generalized event testing framework to more easily write unit tests that codified event firing sequences.
- Part 1 Unit testing event sequences.
- Part 2 Developing an event monitor to test an object’s events.
- Part 3 Using Reflection and MSIL (Intermediate Language) to implement a generic event monitor.
- Part 4 Reflection and MSIL code for dynamic event subscription in detail.
- Conclusion Using the EventMonitor to write [Test]s.
Part 4
In Part 3 of this series we saw how we could use the DynamicEvent class to create event proxies to allow our event monitor to dynamically subscribe to any type’s events. In this last part of the series we describe the DynamicEvent code in detail showing how Reflection and IL are used to create an event handler proxy for implementing dynamic event subscription.

Dynamic event proxy pattern.
The DynamicEvent Class
Subscribing events dynamically is straightforward using DynamicEvent, but the following steps detail what's going on under the hood...
EventPublisher publisher = new EventPublisher();
foreach (EventInfo eventInfo in publisher.GetType().GetEvents())
{
DynamicEvent.Subscribe(eventInfo, publisher, (sender, e, eventName) =>
{
Console.WriteLine("Event raised: " + eventName);
});
}
-
Create the event proxy.
-
Create a dynamic method.
-
Inject the event name and call the event handler.
-
Wrapping it all up.
1. Create The Event Proxy
Firstly, the creation of the event proxy is orchestrated when calling CreateProxyEventHandler.
public delegate void ProxyEventHandler(object sender, EventArgs e, string eventName);
//Useful if we need a reference to the delegate for unscribing an event.
public static Delegate CreateProxyEventHandler(
EventInfo eventInfo, object eventPublisher, ProxyEventHandler handler)
{
Type eventType = eventInfo.EventHandlerType;
bool isStatic = handler.Method.IsStatic;
Type owner = isStatic ? handler.GetType() : handler.Target.GetType();
string eventName = eventInfo.Name;
//Step 2 - Create a dynamic method
DynamicMethod proxyMethod = CreateDynamicMethod(eventType, eventName, proxyOwner);
//Step 3 - Inject the event name and call to our event handler
EmitCode(proxyMethod, isStatic, eventName, handler);
//Step 4 - Wrap the whole thing up in a delegate
Delegate proxyHandler = proxyMethod.CreateDelegate(eventType, handler.Target);
return proxyHandler;
}
//Directly subscribe an event handler.
public static void Subscribe(
EventInfo eventInfo, object eventPublisher, ProxyEventHandler eventHandler)
{
Delegate proxyEventHandler = CreateProxyEventHandler(
eventInfo, eventPublisher, eventHandler);
eventInfo.GetAddMethod().Invoke(eventPublisher, new[] { proxyEventHandler });
}
2. Create a Dynamic Method
Our event proxy is two-sided. Side 1 is called by our event, and side 2 calls into our event handler. For the event side, we must have a method whose signature exactly matches that of the event. As we are dynamically discovering events at runtime, we do not have a matching method in our code, so how do we conjure up a method that has the same signature as our event? We use a dynamic method*.
In preparation for creating our dynamic method, we use reflection to get matching information about the signature of the target event. Having prepared our signature information, we construct our dynamic method. We now have a dynamic method with the same signature as our event.
That’s side 1 of our proxy sorted.
private static DynamicMethod CreateDynamicMethod(
Type eventType, bool isStatic, string eventName, Type owner)
{
const includePrivate = true;
string proxyName = ("Generated proxy for event: " + eventName + ".");
Type returnType = typeof(void);
List<type> eventParameterTypes = new List<type>();
ParameterInfo[] eventParameters = eventType.GetMethod("Invoke").GetParameters();
eventParameterTypes.Add(proxyOwner);
eventParameterTypes.AddRange(eventParameters.Select(x => x.ParameterType));
Type[] parameters = eventParameterTypes.ToArray();
DynamicMethod proxyMethod = new DynamicMethod(
proxyName, returnType, parameters, proxyOwner, includePrivate);
return proxyMethod;
}
* Dynamic methods are created at runtime and provide a shell into which we can emit custom code using IL. Dynamic methods can be wrapped in a delegate and called by any other code – just like a regular delegate. This allows parts of a program to be written whilst the application is actually running – self-modifying code.
3. Inject The Event Name and Call The Event Handler
Now we tackle side 2 of the proxy: the call to our event handler. This is accomplished using IL as we have to generate custom code at runtime.
The IL code sets the call stack up with the arguments to our event handler* (matching the signature of ProxyEventHandler), and then instructs the runtime to call it. Most importantly it is the device used to handle the injection of our event name so that our tests can do their asserting.
The two sides of the event proxy are now completed.
private static void EmitCode(DynamicMethod proxyMethod, bool isStatic, string eventName, ProxyEventHandler handler)
{
ILGenerator il = proxyMethod.GetILGenerator();
if (!isStatic)
{
il.Emit(OpCodes.Ldarg_0); //target object for non-static calls.
}
il.Emit(OpCodes.Ldarg_1); //arg0 - sender
il.Emit(OpCodes.Ldarg_2); //arg1 - eventArgs
il.Emit(OpCodes.Ldstr, eventName); //arg2 - *injected event name*
il.Emit(OpCodes.Call, handler.Method); //call proxy event handler
il.Emit(OpCodes.Ret);
}
* Preparing the arguments for the call stack takes into consideration whether the event subscriber is a static or instance method. All calls to instance methods must have an argument at the first position in the call stack that holds a reference to the instance being called. In a nutshell, the instance reference is passed along by the runtime to the code being executed so it knows what “this” is (static methods do not have a concept of “this”). This is managed by the runtime internally and not exposed at a high level e.g. when programming in C#.
Step 4 - Wrapping It All Up
Now that we have created the dynamic method and emitted our IL code into it, we create a delegate from it. This delegate can be subscribed to an event using reflection.
Delegate proxyHandler = proxyMethod.CreateDelegate(eventType, handler.Target);
eventInfo.GetAddMethod().Invoke(eventPublisher, new[] { proxyHandler });
The signature of the delegate matches the signature of our target event, whilst the arguments pushed onto the stack in our IL match the signature of our test method – the proxy event handler. This combination gives us a proxy that bridges an arbitrary event and our common event handler (see diagram). The injection of the event name into our IL gives us the information about which event has been raised and so allows us to do our testing.
Conclusion
Now we’ve gone over the gory details, we describe how to use the EventMonitor to write unit tests:
