Mocks and fakes and stubs, oh my!

Yesterday, I started writing an article about mocks and fakes and stubs, but ended up writing an article about Inversion of Control (IoC) / Dependency Injection. Today, I’ll take up the topic of how to isolate your code from that of other objects in your unit tests. But, first, what are mocks, fakes and stubs?

In his article Mocks Aren’t Stubs, Martin Fowler defines mocks, fakes, stubs and dummy objects as follows:

  • Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists.
  • Fake objects actually have working implementations, but usually take some shortcut which makes them not suitable for production (an in memory database is a good example).
  • Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test. Stubs may also record information about calls, such as an email gateway stub that remembers the messages it 'sent', or maybe only how many messages it 'sent'.
  • Mocks are objects pre-programmed with expectations which form a specification of the calls they are expected to receive.

All of these objects are useful when unit testing your code. But, each type of test object has it’s own strengths and weaknesses. Let’s look at how to use each type of test object (except for dummy objects) to isolate the Add method from the Logger object that it uses.

Here, again, is the code for the IocCalculator class (modified slightly, to actually throw overflow exceptions):

public class IocCalculator
{
    public IocCalculator(ILogger logger)
    {
        Logger = logger;
    }

    private ILogger Logger { get; set; }

    public int Add(params int[] args)
    {
        Logger.Log("Add");

        int sum = 0;
        try
        {
            foreach (int arg in args)
                sum = checked(sum + arg);
        }
        catch (OverflowException ex)
        {
            Logger.Log("OverflowException: Add");
            throw ex;
        }

        return sum;
    }
}

Dummy Objects

First, let’s try testing this code with a dummy object:

public class DummyLogger : ILogger
{
    public void Log(string message)
    {
    }
}
[Test]
[ExpectedException(typeof(OverflowException))]
public void AddThrowsExceptionOnOverflow()
{
    var calculator = new IocCalculator(new StubLogger());
    calculator.Add(int.MaxValue, 1);
}

Take a look at the dummy object, above. It doesn’t do anything. It just swallows the calls to the logger from the Add method. Is this useful? Actually, yes. It allowed us to test that the exception was thrown correctly. Is it a good fit for this test? Yes. Can it do everything a mock, fake or stub can do? No.

Mock Objects

For example: What if we actually wanted to ensure that the Add method actually called the logger? In that case, a mock object might be more useful:

[Test]
public void AddLogsExceptionOnOverflow()
{
    var mocks = new Mockery();
    var mockLogger = mocks.NewMock<ILogger>();
    Expect.Exactly(2).On(mockLogger).Method("Log");

    var calculator = new IocCalculator(mockLogger);

    Assert.Throws(typeof(OverflowException), () => calculator.Add(int.MaxValue, 1));
    mocks.VerifyAllExpectationsHaveBeenMet();
}

This test replaces the dummy logger object with a mock logger object that the test itself creates (using the NMock framework). The first three lines setup the mock object by instantiating the mock object framework, instantiating the mock logger object from the ILogger interface, and telling NMock to expect two calls to the “Log” method on the mock logger object. Behind the scenes, mockLogger.Log counts the number of times it gets called. The final line of the test then compares the number of times we expected to call mockLogger.Log with the actual number of times it was called.

(Note: The Assert statement above uses a lambda expression to invoke an anonymous method, which in and of itself is a delegate. This syntax was introduced in C# 3.0. If you find it a bit obtuse, you’re not alone. Perhaps it’d make a good blog post. Anyone want to write it?)

One final note on mocks: Many in the Test Driven Development community are enamored with mock objects. Some developers use mocks to the exclusivity of all other types of test objects. Personally, I prefer to do state-based testing rather than behavior based testing. In other words, I want to setup my object under test, call the method under test, and assert that the state of the object changed in some observable way. I don’t want my test to know about the underlying implementation details of the method under test. Mock objects, in my opinion, have a way of getting cozy with implementation details that makes me uncomfortable.

Fake Objects

But, what if you want to test that the Add method logs the correct messages? In that case, you may need to implement a fake logger and validate it’s contents, like this:

public class FakeLogger : ILogger
{
    public readonly StringBuilder Contents = new StringBuilder();

    public void Log(string message)
    {
        Contents.AppendLine(message);
    }
}
[Test]
public void AddLogsCorrectExceptionOnOverflow()
{
    var fakeLogger = new FakeLogger();
    var calculator = new IocCalculator(fakeLogger);

    Assert.Throws(typeof(OverflowException), () => calculator.Add(int.MaxValue, 1));
    Assert.AreEqual("Add\nOverflowException: Add\n", fakeLogger.Contents.ToString());
}

Note that the fake logger is actually a real logger. It actually logs the messages it receives in a string (using a StringBuilder object). Essentially, this implementation is an “in memory” logger, similar to what Fowler described as an “in memory database.” In fact, if this weren’t an example, I would probably have named the class InMemoryLogger or StringLogger, rather than FakeLogger. That’s more in line with what the code actually does.

So, is the fake logger useful? Absolutely. In fact, this is the approach I would actually take, since the dummy and mock loggers cannot check the text that was logged.

Stub Objects

But, what about stub objects? Well, it turns out that I chose a poor example for illustrating stub objects. As you’ll recall from above, stubs return hard-coded values to trigger specific code paths within the code under test. My logger example doesn’t need this kind of functionality. But, if a method were to call the Add method, it might be handy to use a stub to hard code a response, like this:

So, let’s test some code that needs to call the Calculator object. First, here’s the code:

public class Decider
{
    public bool Decide(params int[] args)
    {
        return ((new IocCalculator(new DummyLogger()).Add(args) % 2) == 1);
    }
}

Hmmm… Well, we can’t isolate this code from the IocCalculator code, yet. Let’s refactor:

public class Decider
{
    private readonly ICalculator _calculator;

    public Decider(ICalculator calculator)
    {
        _calculator = calculator;
    }

    public bool Decide(params int[] args)
    {
        return ((_calculator.Add(args) % 2) == 1);
    }
}

Now, we can pass in a couple off different stub objects to test the Decider.Decide method:

public class EvenCalculator : ICalculator
{
    public int Add(params int[] args)
    {
        return 2;
    }
}

public class OddCalculator : ICalculator
{
    public int Add(params int[] args)
    {
        return 1;
    }
}

[TestFixture]
public class DeciderFixture
{
    [Test]
    public void DecideReturnsFalseWhenEven()
    {
        var decider = new Decider(new EvenCalculator());
        Assert.False(decider.Decide());
    }

    [Test]
    public void DecideReturnsTrueWhenOdd()
    {
        var decider = new Decider(new OddCalculator());
        Assert.True(decider.Decide());
    }
}

Conclusion

So, that (finally) concludes my look at mocks, fakes, stubs and dummies. I hope you found it understandable. And, I hope you find it helpful the next time you need to test a piece of code in isolation. In fact, this same Inversion of Control / Dependency Injection approach can be leveraged in many different ways. My focus in these articles was to demonstrate using the approach in unit testing. But, it can also be applied to functional/system testing.

Say you have a website that displays data returned from a web service. Say you want to hide the Address2 line if it is empty. You could look for test data from the web service that meets your needs. Or, you could program your tests to run against a stub service that returns just the values you need. Not only will you be guaranteed that the test data won’t change, but the test will run much faster, to boot. Sure, an end-to-end that calls the actual web service test will be necessary before going live. But, why pay that price every time you run your test suite.

Furthermore, IoC can also be used in production code to enable things like plug-ins or composite user interfaces. In this scenario, an installed plug-in is generally just a piece of code that implements a specific interface – one that allows a third party to develop software that integrates with your code at runtime via configuration.

As always, feel free to post corrections and questions in the comments.

0 responses