When I was first learning Test Driven Development, I recall being pretty confused by the distinction between mocks and stubs. I remember reading explanations that were basically saying that stubs allow you to override the way an object handles a message, while mocks are basically the same thing but which also set an expectation that the overridden message is actually received in the course of the test.
While this made sense on a basic level, I struggled with understanding why you would use one versus the other. Mostly I failed to understand why you would use a stub when mocks can act as this “super stub” that not only gives you control over the executing code, but also act as verification that things are working as you’re expecting them to.
I can’t recall what I read, heard, or saw that finally made this make sense for me, but I wanted to try and explain my thinking as it stands now for others who may be struggling with the distinction.
One metaphor that occurred to me today is of the difference between means and ends. If we step away from worrying about code for a minute and think in terms of normal human interactions, this difference is pretty easy to explain.
When I am at the bar, and I say to the bartender “I’ll have a Manhattan,” the purpose of my statement is to get the bartender to make me a Manhattan. So, simple enough, we would say that the ends of my statement are to request that to the bartender.
What does that leave for the means? Well, the fact that I used my vocal chords instead of a sign language would be one, or choice of words that I used to express myself. These facts are incidental to me getting my drink, they belong to the context (belonging to a species and culture that communicates vocally, having a certain vocabulary and mannerisms in a certain language) I find myself in, but we can draw a distinction between my desire for a drink and the things I have to do to get one. Put me in a jungle in Mozambique and you’d probably observe quite a different range of behaviors in order for me to accomplish the same goals.
What does any of this have to do with mocking and stubbing, you may be wondering? Well, if we were to model the way I behave in this interaction in RSpec, we might come up with something like this:
it "can order a drink from a bartender" do speech = MeansOfCommunication[:speech] manhattan = Cocktails[:manhattan] ben = Human.new ben.desires << manhattan ben.primary_means_of_communication = speech bartender = Bartender.new speech.stub(:phrase_search).with(bartender, manhattan). and_return("I'll have a Manhattan") bartender.should_receive(:order).with("I'll have a Manhattan") ben.order_from(bartender) end
A slightly complicated example, but I wanted to illustrate how the various incidental concerns we identified above are translated as distinct from the essential ones. The essential part of the interaction, that an order was placed expressing my desire, is set up with a mock expectation. The merely incidental part, the means by which I expressed my desire to the bartender, is represented with the stub on the speech object. We don’t really care in this situation how I was able to translate my desire through the medium of speech, what we care about is that expressed my desire to the bartender.
Another way I like to think of the difference between mocks and stubs is something called the Command-Query separation. The rule of thumb is that you should mock commands, while stubbing queries. If we think of this in relation to the example above, it maps directly to our distinction between means and ends. When we think of how we’re going to order the Manhattan, we query our brain, thinking to ourselves “what do I have to say to get this jerk to make me a decent Manhattan?”, and we come up with the right phrase. We use that phrase to make our command to the bartender, which is the essential part of the interaction.
We can understand the prohibition against mocking queries by trying to imagine what it would mean if we wanted to test how we interact with our speech center. Just thinking of phrases all day without saying them to anyone would be purposeless, precisely because it lacks any outward goal. Once we attach a goal to the querying, like coming up with our drink order, we end up implicitly testing our speech abilities through the ends that they are used for.
About the AuthorMore Content by Ben Moss