Why Mock frameworks suck, and how to write delegate-based test doubles

There is a big rift in test-oriented developers about when or how to use mocks, what the difference between a mock, a stub, a fake and a dummy is, and what mock frameworks bring to the game. I won't bore everyone with my explanation of the differences, Martin Fowler is well known for formulating patterns better than anyone else, so I'll point you to his Mocks aren't stubs article. I'll also point to Roy Osherove that has a much more simplified entry about the topic titled Mocks and stubs - The difference is in the flow of information.

To sum it up, mocks are used when you want to test how your object interacts with another object, that's interaction testing, whereas all the others are used when you want to actually test your object's functionality, which I'll consider to be unit testing. Interaction testing depends on more than one area of expertise as it involves your mock being called, and because I don't consider the mock part of the unit, I'd argue that you're not testing a unit.

In the case where you write the code from the ground up, I strongly believe there is no real reason to use mocks, as you can bake dependency injection, interfaces and fakes in your API from the ground-up. When trying to put old code under test however, mocks are a valid approach.

image The gray area is when you want to replace one of your dependencies and use a mock framework to implement a fake. Let's take an example that's quite common, abstracting configuration information. Your first step would be to implement an interface that wraps the initialisation and the property you want to retrieve, which we'll call IConfigurationProvider.

One way I often see being employed is using a Mock framework to do the heavy lifting of implementing the code. As such you'll often see code that looks like the following.

IConfigurationProvider provider = Mocks.DynamicMock<IConfigurationProvider>();
 
Expect.Call(provider.ContentLocation).PropertyBehavior();
Expect.Call(provider.Initialize()).IgnoreArguments().Throw(new ConfigurationException());

My main issue with code like this is the use of a domain language to write code. You see, what you're doing is providing an implementation of an interface, but rather than write the code, we use a mock to write... Well, code. Let's see what it takes to write a fake implementation.

public class FakeConfigurationProvider : IConfigurationProvider
{
    public string  ContentLocation
    {
        get { return null; }
    }
 
    public bool  Initialize()
    {
        throw new ConfigurationException();
    }
}

Obviously that's much more code. Or is it? Let's see a character count:

  • Mock code
    • Characters (no space): 220
    • Characters (with spaces): 260
  • Fake code
    • Characters (no spaces): 163
    • Characters (with spaces): 220

As you can see, what seems at first to be a more compact syntax is actually more verbose. Furthermore, you use an API to write code. I'd say that writing a fake using a mock framework is as useful as writing your unit tests by using reflection.

PS: I'm no Rhino expert and I expect someone to give me a more compact syntax if there's one so I can update this code.

Some people will argue that you can use the Stub<T> method with RhinoMocks to achieve the same with less code. I'd reply that creating my FakeConfigurationProvider once will ensure the same logic implementation is used in every single test that leverage the fake. To which mock fanatics will reply that you will want to change the implementation of one method for a specific test, and my way of writing fakes will create a new class for each test.

So here's my response, and the trick I use very often when writing my tests. We'll use a .net feature that doesn't get used very often, explicit interface implementation, coupled with delegates. For each of the methods in my class, I'll declare a delegate, a public property of the delegate type, together with a value (the default implementation), and finally I'll explicitly implement the interface and call the delegate property. Code speaks better than prose, so here it is.

public class FakeConfigurationProvider : IConfigurationProvider
{
    // Delegate types
    public delegate void InitializeDelegate();
    public delegate string get_ContentLocationDelegate();
 
    // Defining fields for our delegates
    public InitializeDelegate Initialize = delegate { return; };
    public get_ContentLocationDelegate get_ContentLocation = delegate { return null; };
 
    // Implementing our interface
    bool IConfigurationProvider.Initialize()
    {
        Initialize(); // calls the delegate!
    }
 
    string IConfigurationProvider.ContentLocation
    {
        get { return get_ContentLocation(); }
    }
}

Note that by default I don't do anything on Initialize. We'll inject the exception throwing later.

This fake is made up of more code, but the flexibility and clarity gains in your tests is enormous. To proove it, let's create a quick object that uses our configuration provider.

public class ClassThatDoesSomething

{

    public string ContentLocation { get; private set; }

 

    public ClassThatDoesSomething(IConfigurationProvider provider)

    {

        try

        {

            provider.Initialize();

            ContentLocation = provider.ContentLocation;

        }

        catch (ConfigurationException) { }

    }

}

And now let's write a test for the default case where everything goes according to plans in our Initialize method and nothing throws.

[Test]

public void CreatingTheClassSucceeds()

{

    FakeConfigurationProvider provider = new FakeConfigurationProvider();

    ClassThatDoesSomething obj = new ClassThatDoesSomething(provider);

    Assert.IsNotNull(obj); // will never be null anyway, keeping the Assert for more obvious expressiveness

}

As you can see if you run this test, the default implementation of my fake for that method doesn't throw. Now let's see how we can change this to throw exceptions. I've used C# 2.0 delegate notation, with the C# 3.0 notation commented out.

[Test, ExpectedException(typeof(ArgumentException))]

public void CreatingTheClassFailsWhenExceptionIsThrown()

{

    FakeConfigurationProvider provider = new FakeConfigurationProvider();

    provider.Initialize = delegate { throw new ArgumentException(); };

    // C# 3.0 Syntax is shorter:

    // provider.Initialize = () => { throw new ArgumentException(); };

 

    ClassThatDoesSomething obj = new ClassThatDoesSomething(provider);

    Assert.IsNotNull(obj); // will never be null as if something throws we won't reach the assert

}

It only took me one line of code to redefine the implementation of my method. Best of all, I use C# to implement my code rather than use the RhinoMock API. Better yet, I can reuse my default fake implementation to be a fully functional in-memory object that reacts as expected, and inject exceptions in any of the methods of my fake object, which is much more reusable. Even better yet, I'm still enjoying compile-time checks that are not applied with any mock framework.

As someone once said, when I see Mocks being used, I reach for my revolver. You've learned already that the test is written in C#, you know your documentation is (primarily) your tests, now learn that code that replaces code is better done in code!

Ads

Comment