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:

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:

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.

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:

In the first part, I create the Stub using the Stub<T> 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.

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.

A nice write up. This exactly how we ended up writing the first Mock libraries, removing duplication and pushing assertions into the “right” place. Since then, things seem to have got a bit out of hand and we spend a lot of time trying to soften developers’ approaches to using mocks — and trying to get them to listen to what the tests are saying. For example, there’s a relevant piece at http://www.mockobjects.com/2007/05/test-smell-too-many-expectations.html

Oh, and do consider revising the example using Constraints.

Andrei Petrut

Hi,

From my point of view, the unit tests that were created are leaking implementation details. Unit tests should not leak any implementation details but they should instead test the behavior, not the implementation.

Ads

About Me

I'm the chief architect at 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.