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("sender@email.com", "recipient@email.com",
"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("sender@email.com", message.From.Address);
Assert.AreEqual(1, message.To.Count);
Assert.AreEqual("recipient@email.com", 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("sender@email.com", message.From.Address);
Assert.AreEqual(1, message.To.Count);
Assert.AreEqual("recipient@email.com", 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<MailMessage>, giving it a lambda expression that creates a closure 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<T>) 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.

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in TDD, Testing. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://davesquared.blogspot.com David

    I’m really enjoying the Advanced Mocking series — it’s putting a lot of things in perspective for me. Keep up the great work :-)
    Cheers,
    David

  • Jarod

    Great stuff ~

  • http://thom.org.uk/blog Thom Lawrence

    Couldn’t you do this with PropertyIs? I don’t know if there’s a PropertyContains or similar for testing the body of the message, but this seems a little verbose.

  • http://blog.jasonmeridth.com Jason Meridth

    Is there any reason you’re not using the PlayBack using statement in your posts? I have personally forgotten my VerifyAll method call and that has bitten me in the behind.

    Great series by the way.

  • http://jimmybogard.lostechies.com Jimmy Bogard

    @Thom

    Do you have an example? How would the mock test above look with the constraints you’re suggesting?

  • http://jimmybogard.lostechies.com Jimmy Bogard

    @Jason

    If I don’t set expectations, I don’t call VerifyAll. Here I’m not testing interactions, just outputs. It’s an explicit decision I make on what kind of test I’m writing and what kind of test double I’m using that leads me to call VerifyAll or not.

  • http://statestreetgang.net Will

    Fken saved. I’m testing using manually created mocks, but want to start using Rhino. These posts are definitely helping me understand the correlations between the different methods.

  • http://blog.troyd.net/Test%2bSupported%2bDevelopment%2bTSD%2bIs%2bNOT%2bTest%2bDriven%2bDevelopment%2bTDD.aspx Troy DeMonbreun

    I’d recommend TypeMock. For one thing it has the ability to mock “ANY concrete class and ALL members, including public, protected, private, virtual, nonvirtual, constructors and sealed”

    http://www.typemock.com/Product.html

  • http://jimmybogard.lostechies.com Jimmy Bogard

    @Troy

    I’ve never used TypeMock before. Just curious, what would this test look like using TypeMock? How would it be different?

  • http://iridescence.no Fredrik

    Using TypeMock, the above test could look something like this:

    Mock mockedMailSender = MockManager.MockObject();

    mockedMailSenderExpectCall(“SendMessage”).Args(new ParameterCheckerEx(delegate(ParameterCheckerEventArgs args)
    {
    MailMessage message = (MailMessage)args.ArgumentValue;
    // asserts here…
    return true; // or false to fail
    }));

    In this particular example TypeMock isn’t really offering anything RhinoMocks can’t do, though; it’s when you want to mock something that isn’t abstracted by an interface. F.ex, if you didn’t have your IMailSender injection, but just called the SmtpClient class directly, TypeMock could still mock this for you:

    Mock smtpClientMock = MockManager.Mock();
    smtpClientMock.ExpectCall(“Send”).Args(….like before

  • http://jimmybogard.lostechies.com Jimmy Bogard

    @Fredirik

    Thanks for the example. Just curious, since the Mock method is generic, do you have the option for compile-time checking so you don’t need to use strings? I understand that you could only do this with public methods, but the option to do so would be nice.

    Also, does this expectation also subvert the original call? I would expect that by Mocking, the real SmtpMail Send method is never called.

  • http://iridescence.no Fredrik

    @bogardj:

    The fact that it’s generic is merely a convenience for accessing the mocked object through myMock.MockedInstance, which will then be typed instead of just an object. If you want type-safe expectations you need to buy a license for the professional edition of TypeMock, which includes the ‘natural mocks’ functionality.

    By default any call to mocked objects are subverted, but you can control this (including for static/instance constructors). Also, you can use ExpectAndReturn and similar methods to make the mock behave like a stub.

  • Baggio

    As a TDD newbie, I can follow the examples but I find it difficult to grok and differentiate Test Spy, Test Doubles, Mocks, Stubs, Dummy object concepts.

  • http://jimmybogard.lostechies.com Jimmy Bogard

    @Baggio

    Quick note, Test Double is term that encapsulates any kind of alternate implementation created specifically for testing. The other terms are specific types of test doubles.

    http://www.martinfowler.com/bliki/TestDouble.html

    http://xunitpatterns.com/Test%20Double.html

    http://martinfowler.com/articles/mocksArentStubs.html

    I’ll follow up with some real-world examples.

  • Baggio

    That helps. Thanks.

  • http://dotnetslackers.com/community/blogs/simoneb Simone Busoli

    I never used LastCall.Do a lot, but I see that in this case it requires you to write less code, though I tend to find the approach of setting constraints on the method to be more intuitive, like this:

    var body = “Your order is ready!\r\nOrder #:” + order.OrderNumber;

    var message = new MailMessage(“sender@email.com”, “recipient@email.com”, “Order processed”, body);

    LastCall.IgnoreArguments().Constraints(Is.Matching(m => m.From.Address == message.From.Address && m.To.Count == 1));

    Sure, if MailMessage overrides the Equals method it would be even easier:

    LastCall.IgnoreArguments().Constraints(Is.Equal(message));

  • http://dotnetslackers.com/community/blogs/simoneb Simone Busoli

    Oh, with the new release of RhinoMocks you can even do this:

    LastCall.Constraints(Property.AllPropertiesMatch(message));

    so no need to override Equals.

  • http://jimmybogard.lostechies.com Jimmy Bogard

    @Simone

    I’ve never used the constraints, thanks for bringing it up! Do you ever run into any issues related to assertions being alongside the expectations? I’ve never thought of using RhinoMocks to do my assertions as well as expectations. I guess I’m used to seeing “expectations, exercising, verification”.

  • http://dotnetslackers.com/community/blogs/simoneb Simone Busoli

    Using the constraints syntax from Rhino you end up having little or no assertions between exercising and verification, you only set expectations. About issues, I think it mostly depends on how you feel comfortable with. I think that interaction-based testing takes a while to grab and could be harder to read, but once you’ve caught up with it even makes testing faster and requires less code. Actually, not always http://haacked.com/archive/2007/12/09/writing-unit-tests-for-controller-actions.aspx

    BTW, I am always using NBehave for my tests right now, I love the SpecBase class, although I’m using it with NUnit since I prefer using the Resharper test runner which doesn’t have a plugin for NBehave yet. Anyways, guys, keep up the good work with NBehave!

  • http://www.mockobjects.com Steve Freeman

    If you’re going to write about mocks, you should really understand the use of Constraints. They help you focus on the being precise about what matters about the message that one object sends another. I find that a lot of the tests I write have no closing assertions at all, because I’ve said everything in the Expectations.

    I guess we need to make a point of writing up the concept more clearly.

  • http://jimmybogard.lostechies.com Jimmy Bogard

    @Steve

    Thanks for the heads up on Constraints. I’ve found that my style of writing tests (BDD style) prohibits the use of constraints, though.