StovellMocks: Build your own mocking framework

This is an old post and doesn't necessarily reflect my current thinking on a topic, and some links or images may not work. The text is preserved here for posterity.

I am a big fan of Rhino.Mocks, and like all the good tools that I use, I've often wondered how hard it must have been to implement. The two things that intrigued me most were the fluent interface, and where the mock objects come from.

One night I decided to find out. Before looking at the Rhino.Mocks code first though, I figured I'd have a go at implementing the bits and pieces myself, and then I could compare with the real code when I was finished. When you want to learn something, looking at someone else's code first is like reading the last page of a book first - it spoils all the fun :)

Note: this is not a useful mocking framework. I was so proud of all I'd done, but when I eventually looked at the Rhino.Mocks code, I was amazed - it does so much more than I'd even thought of doing. If anything, this process has given me a deeper respect for how mature and feature packed Rhino.Mocks is. In fact, don't even bother reading this post, just go read Rhino.Mocks :)

Building It

The fluent interface is pretty simple. You write code like this:

MockRepository mocks = new MockRepository(); 
IAdder adder = mocks.CreateMock<IAdder>(); 
Expect.Call(adder.Add(3, 7)).Returns(10); 
Expect.Call(adder.Add(4, 7)).Returns(11); 
mocks.ReplayAll();

There are a few parts here to create:

  • MockRepository class. I figured this would just hold a List<IMockObject>, and would have the ability to tell them to "Replay". It can call out to some kind of generator to create the mock object.
  • IMockObject interface. I figured that every mock object would implement an interface which would let the mock framework talk to it. I ended up going away from this approach slightly.
  • Expect class. This is a static class with a few methods, shouldn't be too hard.
  • Expect.Call(). This is a static method that somehow figures out what you were calling, and then returns some kind of interface that allows you to set what you expect the result should be. The big question is: how on earth does it know what you are calling?
  • IMethodCallOptions. Since you're usually invoking a method when you use Expect.Call, I figured the thing that allows you to set the expected return value and other options probably deserves this name. Not sure if it is generic or not though?

Static class or Extension Method?

As I started stubbing out the Expect class, I decided to play with extension methods. This gives me two options - the classic Rhino.Mocks syntax:

MockRepository mocks = new MockRepository(); 
IAdder adder = mocks.CreateMock<IAdder>(); 
Expect.Call(adder.Add(3, 7)).Returns(10); 
Expect.Call(adder.Add(4, 7)).Returns(11); 
mocks.ReplayAll();

Or the new extension method syntax:

MockRepository mocks = new MockRepository(); 
IAdder adder = mocks.CreateMock<IAdder>(); 
adder.Add(3, 7).ShouldReturn(10); 
adder.Add(4, 7).ShouldReturn(11); 
mocks.ReplayAll();

The extension method just calls the Expect class anyway, so if you wanted to use it with Rhino.Mocks, it wouldn't be hard. I personally find it more natural, but I'm probably bias.

Mock Object Implementations

As I started stubbing out classes, I began to have a think about where mock objects come from. I always figured they were probably generated at runtime, but I wasn't sure what that generated code looked like.

For my first try, I wasn't going to bother generating objects yet. Instead, I'd just write the mocks by hand, but in a way that they could be tracked and have expectations set.

First I created an interface which I would mock:

public interface IGstRateProvider 
{ 
    decimal? GetGstRate(string productType); 
    decimal DefaultGstRate { get; } 
}

I experimented with a few different approaches, before settling on something that looked like this. Note that this code is what would be automatically generated at runtime:

public class MockIGstRateProvider : IGstRateProvider 
{ 
    private IMockRecorder _recorder; 

    public MockGstRateProvider(IMockRecorder recorder) 
    { 
        _recorder = recorder; 
    } 

    decimal? IGstRateProvider.GetGstRate(string productType) 
    { 
        return _recorder.MethodCall<decimal?>(
            MethodBase.GetCurrentMethod(), productType); 
    } 

    decimal IGstRateProvider.DefaultGstRate 
    { 
        get 
        { 
            return _recorder.MethodCall<decimal>(MethodBase.GetCurrentMethod()); 
        } 
    } 
}

This was a bit of a change to my original approach. Instead of generated mock objects implementing an interface (IMockObject), they would instead take an interface as a constructor parameter. This probably makes it hard to mock abstract classes, but I only care about interfaces, so I wasn't too worried.

The IMockRecorder interface just has two methods:

public interface IMockRecorder 
{ 
    TReturn MethodCall<TReturn>(MethodBase methodInfo, params object[] arguments); 
    void Replay(); 
}

The first method is called from the mock object to either record a method call, or to replay it, depending on the state of the recording. The second method changes the state from recording to replaying. By doing it this way, the generated mock objects would remain pretty simple.

Expect.Call

As I was implementing the IMockRecorder, I started thinking about how Expect.Call must work. I figured it must return a value but also does some kind of trick so that it knows what was called, so that the method information and arguments could be passed to the Expect.Call method to be recorded. It was at a simple solution hit me.

Since I'm passing the call information to the IMockRecorder, all I have to do is have my IMockRecorder record the call, and stick it in a global variable somewhere. Then my Expect.Call method could pull the information out, wrap it in the IMethodCallOptions interface, and return it. I then reasoned that since my IMockRecorder was created by the MockRepository and linked to it, there was no reason I couldn't store it as a property on the MockRepository. It wouldn't quite be global, but it didn't feel quite right. Since you only record one method call at once though, it seemed OK.

I figured Rhino.Mocks must have a smarter way, but when I took a look, it turned out quite similar: when you call the mock object method, Rhino Mocks stores that as a LastCall against the MockRepository, and the Expect.Call method pulls it back out. Phew, I wasn't too far off :)

What this meant from the Expect.Call method's perspective is that although it took a parameter, it didn't really care about the parameter - that was just a return value from the method that was being recorded (default(T) when recording). My method ended up looking like this:

public static IMethodCallOptions<TReturn> Call<TReturn>(TReturn ignored) 
{ 
    IMethodCall methodCall = MockRepository.Current.LastMethodCall; 
    return (MethodCall<TReturn>)methodCall; 
}

The MethodCall<TReturn> class implements IMethodCallOptions<TReturn>. IMethodCall, on the other hand, is used internally. It feels a bit clunky at this point, so I might try to tidy that up.

At this point I almost had something working. All I needed to do was to implement the MockRepository.ReplayAll() method.

My plan for the MockRepository was to store a list of all the mock objects it created. But since my mock objects weren't implementing a specific interface, I had to change it to store the IMockRecordersinstead. ReplayAll() could then just loop through all the recorders and tell them to Replay(). Here's what it looks like:

public TMock CreateMock<TMock>() 
{ 
    IMockRecorder recorder = new MockRecorder<TMock>(); 
    _mockRecorders.Add(recorder); 

    return MockObjectFactory.CreateMock<TMock>(recorder); 
}

public void ReplayAll() 
{ 
    foreach (IMockRecorder mock in _mockRecorders) 
    { 
        mock.Replay(); 
    } 
}

At this point I was able to write a basic test case. I had to create the mock objects by hand, using the solution above, but I was able to record/replay to my hearts content:

// Setup the expectatons of the mock GST rate provider 
MockRepository repository = new MockRepository(); 
IGstRateProvider provider = repository.CreateMock<IGstRateProvider>(); 
provider.GetGstRate("Bread").WillReturn(0.00M); 
provider.GetGstRate("Milk").WillReturn(0.00M); 
provider.DefaultGstRate.WillReturn(0.10M); 
repository.ReplayAll(); 

// Test the GST Calculator using the mock provider (instead of, say, the database 
// GST rate provider it would use in production). 
GstCalculator calculator = new GstCalculator(provider); 
Assert.AreEqual(2.57M, calculator.ApplyGst("Bread", 2.57M), "Bread should be exempt from GST"); 
Assert.AreEqual(4.00M, calculator.ApplyGst("Milk", 4.00M), "Milk should be exempt from GST"); 
Assert.AreEqual(2200M, calculator.ApplyGst("Laptop", 2000M), "Laptops should not be exempt from GST"); 
Assert.AreEqual(26400M, calculator.ApplyGst("Car", 24000M), "Cars should not be exempt from GST"); 
repository.VerifyAll(); 
Console.ReadKey();

And that night, I released it to the masses on Readify Tech.

MockObjectFactory

I figured I might use System.Reflection.Emit to generate the mock objects, rather than hand writing them, but having not used it before I was tempted to just leave it how it was. It wasn't until Corneliu sent me his Dynamic WCF ClientProxy class. It uses Reflection.Emit to generate all the boilerplate code WCF client method calls. Looking at his code, it didn't seem that hard at all, so tonight I gave it a go.

Since I'd written the mock objects by hand, all I needed to do was compile them, and open them in Reflector and view the IL that was generated. Then I just translated those IL statements into Reflection.Emit calls. The whole thing took about 2 hours of fiddling around having not used the API before, and when all was said and done it totalled just over 160 lines of code. Here's an example of how a method is generated:

private void ImplementMethod(MethodInfo methodToImplement) 
{ 
    IEnumerable<Type> parameterTypes = methodToImplement.GetParameters()
        .Select(p => p.ParameterType); 
    MethodBuilder methodBuilder = _typeBuilder.DefineMethod(methodToImplement.Name, 
        MethodAttributes.Public | MethodAttributes.Virtual, methodToImplement.ReturnType, 
        parameterTypes.ToArray()); 
    methodBuilder.CreateMethodBody(null, 0); 
    ILGenerator methodILGenerator = methodBuilder.GetILGenerator(); 
    LocalBuilder resultLocalBuilder = methodILGenerator.DeclareLocal(methodToImplement.ReturnType); 
    LocalBuilder parametersLocalBuilder = methodILGenerator.DeclareLocal(typeof(object[])); 
    methodILGenerator.Emit(OpCodes.Ldarg_0); 
    methodILGenerator.Emit(OpCodes.Ldfld, __recorder0); 
    methodILGenerator.Emit(OpCodes.Call, GetMethodBaseGetCurrentMethod()); 
    methodILGenerator.Emit(OpCodes.Ldc_I4_1); 
    methodILGenerator.Emit(OpCodes.Newarr, typeof(object)); 
    methodILGenerator.Emit(OpCodes.Stloc_1); 
    methodILGenerator.Emit(OpCodes.Ldloc_1); 
    for (int parameterIndex = 0; 
         parameterIndex < methodToImplement.GetParameters().Length; 
         parameterIndex++) 
    { 
        methodILGenerator.Emit(OpCodes.Ldc_I4, parameterIndex); 
        methodILGenerator.Emit(OpCodes.Ldarg, parameterIndex + 1); 
        methodILGenerator.Emit(OpCodes.Stelem_Ref); 
    } 
    methodILGenerator.Emit(OpCodes.Ldloc_1); 
    methodILGenerator.Emit(OpCodes.Callvirt, 
        GetIMockRecorderMethodCallFor(methodToImplement.ReturnType)); 
    methodILGenerator.Emit(OpCodes.Stloc_0); 
    methodILGenerator.Emit(OpCodes.Ldloc_0); 
    methodILGenerator.Emit(OpCodes.Ret); 
    _typeBuilder.DefineMethodOverride(methodBuilder, methodToImplement); 
}

Summary

Doing this experiment gave me a feel for the kinds of challenges that Rhino.Mocks and TypeMock must have faced, and why certain decisions were made in the API's. It also gave me a sense of just how much work must have gone into those frameworks. It's certainly not for the feint hearted.

That said, the whole thing didn't take too long to build. The entire solution (with comments, blank lines, etc. included) came out to just over 800 lines, which is more than enough to go live with. I spent three hours one night, and three hours the next night. That's not a bad price for learning a few lessons:

  • Fluent interfaces are easy once you decide on what the language will look like. I suspect designing the language is half the battle.
  • Reflection.Emit is easy, just a little time consuming. Try to emit as little as possible.
  • Extension methods are a good way to make anything look nice :)
  • Don't challenge Ayende or Corneliu to a coding competition
  • Don't roll your own mocking framework
  • Trying to code things yourself before looking is a great way to test your own skills

If you're interested in checking out the code more, you can browse it using the Subversion web interface:

TODO