Using Mocks to Verify Interactions

[article]
Summary:

In the March 2006 issue of Better Software magazine, Dan North began a discussion of the evolution of behavior-driven development from test-driven development. Here, North continues the conversation with closer look at "mocks," utility classes that, for testing purposes, pretend to be some component or service with which your object will interact.

In my March 2006 Better Software magazine article, "Behavior Modification," I introduce behavior-driven development (BDD) as an evolution of established agile practices, including test-driven development (TDD). Using TDD, you write a test method to describe the behavior you are about to implement, next you write the code that implements the behavior, finally you run the test method to make sure everything works.

Typically the test executes some code then, using JUnit's Class assertEquals(...) family of methods, it asserts that certain fields or properties have expected values. The problem with this is that often by the time you get to assert anything, you've already missed all the action! For instance, if an object is expected to send an email or display something on a screen, after the fact it can be difficult to verify anything useful based on the state of some objects. You could set up expensive infrastructure like a mail gateway and a client inbox to verify against, but that isn't very appealing for a single test.

A mock is a utility class that attempts to address this problem. The mock pretends to be some component or service with which your object will interact. You can set it up with stubbed values in the form of canned responses to methods ("if anyone asks how much is in the account via the getAccountBalance() method, respond with $300"). You can also tell it to expect certain method calls ("someone should call the sendEmail(email) method with an email to fred@flintstone.org").

If any unexpected methods are called on a mock, it causes the test to fail. At the end of the test, the mock ensures it has been called as expected and causes a failure if any method invocations were missed.

In the article, I mentioned that I wrote a test to ensure that a ClientDetailsValidator interacted correctly with an AgeCalculator. Whenever I write a test that verifies interactions, I start with the following template:

public void shouldDoSomething() {
m// given...
m// expect...
m// when...
m// then...
}

It reads: given some initial setup, expect some interactions, when I execute some behavior, then ensure the outcome. This causes you to think about the behavior in stages. For this example, I had:

public void shouldCalculateAge() {
m// given...
m// I create a mock AgeCalculator

m// I create a ClientDetailsValidator
m// passing in the mock calculator
m// via its constructor

m// I set an age and a date of birth
m// in the validator

m// expect...
m// the "calculateAge" method to be
m// invoked with the age and date of
m// birth I set in the validator

m// when...
m// I execute the validator

m// then...
m// ensure the expected method was called
}

To be able to verify the interaction, I replaced the real AgeCalculator with a mock. In this example, the mock was set up with a single expectation (the calculateAge method should be called with particular values) and injected into the ClientDetailsValidator via its constructor. Mocks are particularly useful as you adopt the strategy of "discovering" dependencies, like I did with AgeCalculator.

But I'm not satisfied with the word "mock." It doesn't describe the clever dual role that mocks play, as both a pretend object and something that is aware that it is a pretend object. I am leaning toward the term "actor." If I have an actor for an AgeCalculator, I can either talk to it as an actor--setting up its script and such--or (quite literally) cast it

About the author

Dan North's picture Dan North

Dan North is a senior consultant with ThoughtWorks, where he coaches development teams in agile software delivery and project automation. A programmer with fifteen years of delivery experience, Dan has published a number of articles and spoken at conferences on topics ranging from agile enablement to NLP.

StickyMinds is one of the growing communities of the TechWell network.

Featuring fresh, insightful stories, TechWell.com is the place to go for what is happening in software development and delivery.  Join the conversation now!