Advanced mocking: mocks and stubs


Test Spies can help us verify the indirect outputs of a system under test.  If an OrderProcessor needs to send an email, we don’t want that test sending real emails, so we set up a Test Spy to capture that indirect output so we can verify it inside our test.

Sometimes a Test Spy isn’t enough, as our test might have indirect inputs as well as indirect outputs.  An indirect input is an input variable or value that a method uses that doesn’t appear in any object passed in to the method.

In the previous Test Spy example, our business partners requested that an email be sent out for each order processed.  That was yesterday, and today, they have another request.  Sometimes, orders are sufficiently large that they would like a sales agent to also be notified by email, so that they might follow up with that customer and try to wring some more sales out of the customer’s pockets.

Two conditions arise: the order maximum might change, and it might depend on the customer placing the order.  After some more conversation, it turns out that the logic behind whether an Order should be emailed to a Sales Rep or not is fairly complex.

So instead of embedded this complex logic inside our order processor, we’ll extract it into a Specification:

public interface ISpecification<T>
{
    bool IsSatisfiedBy(T value);
}

That Specification provides the indirect input that necessitates using a full-blown Mock Object.

Examining the class under test

With a Mock Object, we want to test both indirect inputs and outputs.  To do this, we can modify our original Test Spy to capture the input and provide a canned output.  To put this all in context, let’s take a look at our OrderProcessor:

public class OrderFulfillmentService
{
    private readonly IMailSender _mailSender;
    private readonly ISpecification<Order> _salesSpec;

    public OrderFulfillmentService(IMailSender mailSender, ISpecification<Order> salesSpec)
    {
        _mailSender = mailSender;
        _salesSpec = salesSpec;
    }

    public void ProcessOrder(Order order)
    {
        // hit database, whatever

        //send email
        if (_salesSpec.IsSatisfiedBy(order))
        {
            string body = "Huge frickin' order alert!!!rnOrder #:" + order.OrderNumber;

            MailMessage message =
                new MailMessage("[email protected]", "[email protected]",
                                "Order processed", body);

            _mailSender.SendMessage(message);
        }

    }
}

When the Order satisfies the SalesSpec, the OrderProcessor should send an email to our Sales Rep.  Normally, I’d write the tests before modifying the class under test, but it’s a little easier to see the scope of the change after it’s already made.

Here come the mocks!

As with all Test Doubles, we have a motivation to supply an imposter dependency to our class under test.  In this case, the real IMailSender will send emails (which we don’t want) and the real ISpecification has complicated logic (which we want to keep separate).

For the ISpecification, not only do I want to test the indirect outputs (the Order passed in), but I want to verify the behavior of the system from its indirect outputs.  Going back to the original context and specification, “When the Order satisfies the SalesSpecification, it should send an email to the SalesRep”.  Note the “should” part, we want to make sure that the IMailSender was actually called.

The manual way

When we care that the interaction between two components, we capture both the messages going back and forth and the number of times the interaction was used.  Our IMailSender mock will capture these two pieces of information and expose them for examination later:

public class MailSenderMock : IMailSender
{
    public MailMessage Message = null;
    public int SendMessageCallCount = 0;

    public void SendMessage(MailMessage message)
    {
        Message = message;
        SendMessageCallCount++;
    }
}

Looking at the ISpecification, I’m only verifying the interaction between the OrderProcessor and the IMailSender, so I’ll just hard-code a specific result into a Test Stub:

public class OrderSpecificationStub : ISpecification<Order>
{
    private readonly bool _result;

    public OrderSpecificationStub(bool result)
    {
        _result = result;
    }
    
    public bool IsSatisfiedBy(Order value)
    {
        return _result;
    }
}

Note that I always give very intentional names to my Test Doubles.  Folks coming back and looking at my tests should be able to understand what I’m testing by the name of my fixtures, test methods, and variable names.  It never hurts to be as explicit and clear as possible.

With these two Test Doubles in place, I’m ready to create my test:

[Test]
public void Should_send_sales_email_when_order_satisfies_sales_spec_manually()
{
    bool result = true;
    MailSenderMock mailSenderMock = new MailSenderMock();
    OrderSpecificationStub orderSpecStub = new OrderSpecificationStub(result);

    OrderFulfillmentService service = 
        new OrderFulfillmentService(mailSenderMock, orderSpecStub);

    Order order = new Order();
    order.OrderNumber = "8574-PO8468371";

    service.ProcessOrder(order);

    MailMessage message = mailSenderMock.Message;

    //Verify expectations
    Assert.AreEqual(1, mailSenderMock.SendMessageCallCount);

    //Verify outputs
    Assert.IsNotNull(message);
    Assert.AreEqual("[email protected]", message.From.Address);
    Assert.AreEqual(1, message.To.Count);
    Assert.AreEqual("[email protected]", message.To[0].Address);
    StringAssert.Contains(order.OrderNumber, message.Body);
}

I test both the expectations (the number of times the method was called) as well as the indirect outputs (the MailMessage).  With this test, I know with absolute certainty that when the Order satisfies the SalesSpecification, the MailSender is used to send an email to the sales rep.

When this test passes, I’ll create an additional test to cover the scenario where the Order doesn’t meet the SalesSpec specification.  In that case, I’ll just make sure that the SendMessageCallCount is zero and be done.

Again, all these little classes can get annoying to maintain over time.  Although they’re very explicit in their scope, if each fixture has 3-5 TestDouble implementations, they can really add up when your tests get into the thousands.

Enter Rhino Mocks

With Rhino Mocks, I can create both Test Stubs and Mocks.  It’s pretty important to understand the different types of Test Doubles before jumping into a tool like Rhino Mocks, as excessive Mock Objects can lead to very brittle tests.  I rarely create more than one Mock Object in a single test, although I might have several different Test Doubles going on.

Although the name is deceiving, Rhino Mocks can create all sorts of Test Doubles, including Test Stubs and Mock Objects (obviously).  With Rhino Mocks:

[Test]
public void Should_send_sales_email_when_order_satisfies_sales_spec()
{
    MockRepository mocks = new MockRepository();
    ISpecification<Order> salesSpec = mocks.Stub<ISpecification<Order>>();
    IMailSender sender = mocks.CreateMock<IMailSender>();

    MailMessage message = null;

    Order order = new Order();
    order.OrderNumber = "8574-PO8468371";

    using (mocks.Record())
    {
        SetupResult.For(salesSpec.IsSatisfiedBy(order)).Return(true);

        sender.SendMessage(null);

        LastCall
            .IgnoreArguments()
            .Do(new Action<MailMessage>(actual => message = actual));
    }

    OrderFulfillmentService service = new OrderFulfillmentService(sender, salesSpec);

    service.ProcessOrder(order);

    mocks.VerifyAll();

    Assert.IsNotNull(message);
    Assert.AreEqual("[email protected]", message.From.Address);
    Assert.AreEqual(1, message.To.Count);
    Assert.AreEqual("[email protected]", message.To[0].Address);
    StringAssert.Contains(order.OrderNumber, message.Body);
}

In the first part, I create the Stub using the Stub method on the MockRespository.  The Rhino Mocks stubs are nice as they provide out-of-the box property implementation.  Next, I create the Mock Object using the CreateMock method.  This creates the proxy class that will intercept my calls for verification later.  It performs much of the same functionality of my manually created Mock Object, but much more flexible.

In the Record section (mocks.Record() part), I set up all of the results and expectations of my Test Doubles:

  • Setup a result for the Test Stub
  • Set up an expectation for the Mock Object
  • Capture the input of the Mock Object (the indirect output of the OrderProcessor)

In the exercise portion, I create the OrderProcessor and pass in the Test Double implementations from Rhino Mocks.  To get the ball rolling, I call the ProcessOrder method, and I’m ready to start verifying.

Finally, in the verification section, I first call the VerifyAll method on the MockRepository.  This call verifies all the expectations have been met, such as all methods were called that I set up, and no extra ones were called that I didn’t set up.  I finish up the verification by performing assertions on the indirect output MailMessage I captured earlier.

Small sidenote on expectations and verifications

When using Mock Objects, your tests have a distinct pattern to them:

  • Set expectations
  • Verify expectations

In the first section, I set up all the results and method call expectations I expect to be called when exercising the class under test.  In the verification section, Rhino Mocks examines the expectations versus what actually happened, and fails my test if it doesn’t match up.

So why not use the real implementations?

Most of the backlash against Mock Objects is misdirected against Test Doubles.  Test Doubles are absolutely vital in narrowing the scope of failures when something goes wrong.  If a test fails but it calls into a hierarchy of a dozen components, how much time do I waste in trying to hunt down the real cause of failure?  More than 30 seconds figuring out a test failure is too long.

Mock Objects can be too powerful and overkill in most situations.  If my test fails because something was called and I didn’t set up the expectation, this tends to lead to tests that exactly match implementations.  That’s a test smell of an over-specified test.  While tests are supposed to make it easy to change code, if a removing a method call causes an entire fixture and a dozen tests to fail, I haven’t really gained anything.

The trick is to know the different types of Test Doubles out there, and test what you mean to test.  Accidental verifications and assertions by making every Test Double a Mock Object leads to brittleness.  Pick the right Test Double for the situation, and your tests will be giving you the value they’re supposed to.

A small correction