Hot in the Test-Driven Development Yahoo group this week is a discussion concerning the perceived continuum between the so-called "Classic" and "Mockist" approaches to TDD. Steve Freeman, Nat Pryce, Michael Feathers, Dale Emery, and many more discuss terminology and describe their approaches. The discussion also debates whether there even really exists such a continuum, and if so, what distinguishes the approaches that represent it's extremes?
After stating he "feels inclined to the classic-TDD approach", Olaf Bjarnason started the 70+ entry discussion with this question to the group: "What made you switch [to "mockist"] from classic, if you were there before? How does the new method feel?". Many of the early responses though centered more around a challenge Olaf's thread title, "Classic/Mockist discussion". Is there really a need for such a binary distinction?
Nat Pryce, co-creator of JMock, expresses his feeling:
I think the whole division of TDD practice into "mockist" vs "state based" is pointless, distracting and does a disservice to people trying to learn and use TDD.
Mock objects are just a tool. They are one of many tools that you need to use when doing TDD. Like any tool, they are designed to help solve a set of problems in a specific context. Outside that context they do not help and can be a hindrance.
If this is the case, how then does one know when to apply this "tool"? How does one know to test "state", namely without a mock, versus asserting behavior, namely by using mocks?
Dale Emery chimes in to describe how he typically makes the distinction:
Lots of people use the distinction of "state-based" versus "behavior-based." I use a twist on this. I think "result" versus "protocol". If the essence of the test is whether the UUT produced the correct result, I don't usually need a mock for that. If the essence of the test is whether the UUT properly executed its role in a protocol--i.e. whether it sent the right messages to the right collaborators, given the starting conditions and the stimulus--I use mocks to represent the UUT's direct collaborators.
Lior Friedman asserts that a mock's place is in representing a contract:
For me usage of mocks indicate the existence of a "contract" between the class under test and another class which is used. And the goal of the test is to verify that the contract is fulfilled.
Charlie Poole pitches in with his gauge on when state-based is the way to go:
I'd use state-based testing on an object if I had no other way to observe it's behavior - that is if /all/ the call did was change an observable state of the object. Assuming the object actually did something, I'd want to verify [with a mock] that it did it.
Adam Sroka on when he uses a mock:
For my own part, I *always* mock at system defined boundaries (e.g. filesystem, network, database, etc.) I usually mock when I want to elicit loose coupling between an interface and it's client in a top-down direction. And, I generally don't mock when the object I am interacting with is small and easily faked/stubbed.
As the thread goes on, and as may be highlighted by the above sound-bites from it, there is a recurring observation that while many of these ideas are similarly themed, they don't seem to share a commonly categorizable, named approach beyond "We do it sometimes, but not others". Further, each (with the exception possibly of Sroka's statement about "top-down") tend more to the following: The decision to mock is being driven by the design, as opposed to the converse, where the design is driven by the mocks.
Regarding this idea of using mocks to drive design, Michael Feathers gives an example:
To me, the real meat behind the whole subject is just how far people are willing to push "tell, don't ask." Mocks are a prop for a design approach.
...
Imagine you have some object and you want to get report of its errors:
class Errors {
int errorCount();
Error getError(int index);
}
Errors errors = object.getErrors();
That's an ask. We can do state-based testing of the Errors object in the test.
To make it a tell you'd move to this:
interface ErrorReceiver {
void accept(Error error);
}
ErrorReceiver receiver = ...;
object.reportErrors(receiver);
We can set expectations on a mock receiver and pass it during testing and use a "real" class in production.
Later, largely in response to a post by Steve Freeman (another co-creator of JMock) describing his partner Nat Pryce's categorization of some "mock-worthy peer objects" ("Dependencies","Notifications", and "Policies"), Feathers (and also Colin Jack) assert that the design ideas the duo have been putting forth in fact exist at the core of this "mocks as a tool to drive design" philosophy. Further, more to the point of this thread, that when people talk of a "mockist TDD approach" they are referring in large part to these design ideas, and that the recurring confusion about "Classic vs Mockist TDD" could be reduced if these collection of ideas had a more concrete name.
As Pryce reminds the group, he and Freeman have a book in the works that may just provide some this clarification being requested. He also points the group to a write-up of his on "State vs Interaction Based Testing".
So, keeping it real, this conversation is really not a new one (Google it), but it is it seems a recurring one. Is there a "Classic TDD", and does it mean "Design begets Mocks"? Is it characterized by something else? Is there "Mockist TDD"? Is it characterized by a "Mocks beget Design" philosophy? "Tell, Don't Ask"? Is it something altogether else? Either way, is it really a "this OR that" thing, or more likely "this for these AND that for those"?
Of course, as always, this news is simply a [hopefully objective] highlight of the Yahoo group discussion, and as such only a piece of the puzzle. Read it for yourself, as well as other sources, and tell others here and/or in the discussion what your experience says.