Advanced mocking: anonymous test spy


When practicing TDD, I create interfaces to abstract away the ugly stuff, like databases, file I/O, web service calls, email messages, and other external systems.  I can run tests now that don’t have to interact with real implementations of these external components, which may or may not be available all the time or may take a long time to run in practice.

But when testing a class that uses these components, I often want to verify messages or objects passed to these fake components.  Unfortunately, these messages are created internally in the method, and I don’t have a way to access them directly.  For example, consider the OrderFulfillmentService, which needs to send emails when orders are fulfilled:

public class OrderFulfillmentService
{
private readonly IMailSender _mailSender;

public OrderFulfillmentService(IMailSender mailSender)
{
_mailSender = mailSender;
}

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

//send email
string body = "Your order is ready!rnOrder #:" + order.OrderNumber;

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

_mailSender.SendMessage(message);
}
}

What I’d like to do is verify the MailMessage being sent to the IMailSender has the correct sender and recipient email addresses, and maybe that the body contains the OrderNumber.  But the creation of the MailMessage is internal to the method, and I really don’t feel like extracting all of that out to a different public method or class if I don’t need to.

All I really care about is that when the OrderFulfillmentService processes an order, the correct email gets sent out.  If I extract the email creation to a separate class, I still have to write additional tests to ensure that the correct email message gets sent out, so I’m not that much better off going that route.

If I could somehow intercept or catch the message being sent to the IMailSender in a test, I could verify its contents.  That’s where the Test Spy pattern can help out.  Both with and without Rhino Mocks, I can intercept this indirect output.

Here come the mocks!

Since we don’t want real emails being sent out during testing, we’ll create a test double to stand in the IMailSender’s place.  But the test double we use will be special, as we want to create a test to check the contents of the indirect outputs of the MailMessage.

To do this, we’ll create a Test Spy that snags the MailMessage and sticks the result into a local field that we can look at later in the test:

public class MailSenderTestSpy : IMailSender
{
public MailMessage Message = null;

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

Pretty simple little class, I just stick whatever gets passed in to a local field.  So what will the test look like?  Here’s what I came up with:

[Test]
public void Should_create_order_details_email_manually()
{
MailSenderTestSpy sender = new MailSenderTestSpy();
OrderFulfillmentService service = new OrderFulfillmentService(sender);

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

service.ProcessOrder(order);

MailMessage message = sender.Message;

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);
}

The setup of the OrderFulfillmentService is normal, except I pass in an instance of my test spy (soooo sneaky).  After calling the ProcessOrder method, I grab out the MailMessage.  Finally, I verify the contents of the MailMessage according to the customer’s specifications.

Creating the Test Spy manually is all well and good, so how might we create one in Rhino Mocks?

Enter Rhino Mocks

In most mocking scenarios, I set expectations that methods will get called, and later verify these expectations.  In the Test Spy case, I not only want to set expectation that the method will get called, but I want to additionally grab the MailMessage when the mock gets called.  Luckily, I can supply extra behavior with the Do() method in Rhino Mocks.  Let’s take a look at the test:

[Test]
public void Should_create_order_details_email()
{
MockRepository mocks = new MockRepository();
IMailSender sender = mocks.CreateMock<IMailSender>();

MailMessage message = null;

using (mocks.Record())
{
sender.SendMessage(null);

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

OrderFulfillmentService service = new OrderFulfillmentService(sender);

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

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);
}

Instead of creating my custom little Test Spy class, I use RhinoMocks and the CreateMock method to create a mock implementation of the IMailSender.  Rhino Mocks creates an implementation at runtime, so I never have to worry about managing this extra test double class.

In the Record phase, I call the SendMessage method on the IMailSender, but I pass in null.  This looks strange, don’t I actually want something sent in?  In normal mocking scenarios where I’m verifying the arguments, I would pass the real arguments I expect would be used by the class under test.  But since the MailMessage is created inside the ProcessOrder method, I don’t know what the MailMessage is.

In the next part, I use LastCall to get at the last method call to a mock object (in this case the SendMessage) and start applying expectations and options.  I IgnoreArguments, as I don’t want Rhino Mocks to care about the null that was passed in.  That’s why the null doesn’t matter, I explicitly tell Rhino Mocks not to care.  Finally, I use the Do method to supply a custom [Action](http://msdn2.microsoft.com/en-us/library/018hxwa8.aspx), giving it a [lambda expression](http://msdn2.microsoft.com/en-us/library/bb397687.aspx) that creates a [closure](http://diditwith.net/PermaLink,guid,235646ae-3476-4893-899d-105e4d48c25b.aspx) and captures the MailMessage that got passed in.

####

Quick sidenote on the magic

Yes, I know that was a mouthful, but the basic idea is I want to create a Delegate object that matches the signature of the method I just called (SendMessage).  The signature of this method is:

void SendMessage(MailMessage message);

Since Rhino Mocks expects an instance of a Delegate object (and not just any old delegate), I can’t pass in the lambda directly.  Instead, I have to create an instance of a delegate type and pass that in.  I can create a delegate type directly:

public delegate void SendMessageDelegate(MailMessage message);

But instead, I’ll use the built-in generic delegates (Action) that match my method.  For void methods, I use the Action delegates, and for methods that return values, I use the Func delegates.

Finally, I create the lambda expression (I could have used an anonymous delegate, or just the name of a local method that matches that signagture).  This is a special lambda expression that has access to the local scope, so I can snag the MailMessage sent in to the lambda and set it to a local variable (this is the closure part).

####

Back to the test

Skipping past the first part, we can see from the OrderFulfillmentService instantiation on down, the test looks much of the same as the first one.  The lamdba part can be strange to look at the first time, but it’s a succinct way to pass in a custom snippet of behavior.  When the SendMessage method gets called by the ProcessOrder method, Rhino Mocks will route that call to the lambda expression I gave it.  This lambda expression takes the MailMessage passed in and assigns it to a local variable.

I finish up the test by checking the MailMessage contents, and the test passes just as well as the first example.  This time, no extra test spy class hangs around.

Wrapping it up

Test spys are great for capture indirect outputs of operations.  The MailMessage created above couldn’t be observed directly as I didn’t return it out of the method, so I had to create a special class to snag the MailMessage that got passed in.  Later, I could examine the MailMessage capture and verify its contents to my hearts desire.

I did this with both a custom Test Spy class and with Rhino Mocks.  Use whatever one is most clear to the reader of the test, but Rhino Mocks can be helpful in reducing the number of test double classes that can pile up.  Since each test in one fixture might use a different kind of test double, using Rhino Mocks eliminates the extra overhead of the test double implementations.

Advanced mocking: auto-interaction testing