testing-concept-fakes-instead-of-stubs-or-mocks

The idea that you use the features of the language to allow you to create fake objects rather than using the stubbing facilities of your testing tool.

podcast-rr-269-testing#fakes-instead-of-stubs-or-mocks

At 44:

CHUCK: I kind of want to dig into the external… I mean you're talking about a deployment process so there are all these external things that you're touching, right? So, how do you write your tests around things that you're touching that aren't part of the local system?

JESSICA: I fake them.

CHUCK: Okay.

JESSICA: Mostly.

CHUCK: And that is different from mocking or stubbing how?

JESSICA: Okay. Alright, alright, alright. Say for instance we interact with Git for instance. I'm going to add a fake Git client or I'm going to add a Git client interface that knows how to do things like tell me what branch we're on or tell me whether this commit is in the history of this other commit. And I'm going to make those into methods instead of specific Git commands. And then I'm going to be able to write an implementation of that Git client interface that fakes that out. That I can have it behave however.

Now some people will use a mock to implement this. And the mock… so, the evil thing about the mock is you tell it, "Okay, expect this method call with these arguments and then return this other thing." And it's like making sure that method call happened exactly so. But that's not what I'm trying to test, exactly what method was called on what. Because I don't actually care. I care about the result. And if I can put the interface at a meaningful point that describes the outside interaction and then fake the outside stuff just enough to get my code to work, then I'm not testing the implementation detail of exactly what method. I'm being explicit in my test about what I care about and not trying to specify every method call everywhere. Ah. Does that make sense?

CHUCK: I'm trying to think of a way to restate what I heard…

JESSICA: Alright. Yeah.

CHUCK: And then you can tell me that I got it wrong. And essentially what I heard was that instead of creating a mock object that is essentially duck typed to whatever APIs I'm going to call, you actually create some kind of object or class that implements the interface that you're going to need and then you effectively inject it into whatever you're testing so that it doesn't matter necessarily that I know as part of my test that the internals of what I'm testing are going to call these methods on this mock object. Instead I just have a fake that responds to all the right things. And so, it just does what it's supposed to do without me having to explicitly tell it that these methods exist every time I want to test that particular interface.

JESSICA: Yeah, that is true. Let me try again.

CHUCK: [Laughs]

JESSICA: Either way I'm going to set up an interface that separates my code from the outside world. And there's a real implementation that talks to the actual outside world. And then my preference is a fake that mimics the outside world just enough to let my code work. A mock on the other hand is very specific about exactly what method my code must call with precisely these arguments and precisely this number of times.

CHUCK: [Inaudible]

JESSICA: And I don't actually care about that. I…

SAM: Mm, okay.

JESSICA: I want my fake to just let my code work so I can test what I'm trying to test. I don't want to be any more specific than that.

SAM: Okay. So, I actually can see a point where I would use both of those. And in a case where I'm directly testing… like say I have an adapter that calls out to another microservice. So in my code, all of my Ruby code calls to this one adapter and then that adapter makes HTTP calls over the network. So, when I'm testing that adapter directly I want to mock it. I want to say that when I call this method it makes these three calls in sequence with these parameters because I'm being overly cautious about [chuckles] what this thing is going to send across the network. So, when I'm looking at that adapter directly then yeah, I want to use mocks. But everywhere else where the adapter is just incidental, yeah totally I will replace that with a… or I'll just in Ruby I'll just stub out a method on that adapter and say, "It doesn't matter if this gets called or not."

CHUCK: Yeah.

SAM: Does that maybe capture what you're going for?

JESSICA: Yeah. Except I don't test my adapters. I mean…

[Laughter]

JESSICA: I don't unit test my adapters. I will integration test them with the real method, the real thing.

SAM: Really? I avoid the remote integration tests as much as possible because maybe that's my background of working in places where it's hard to set up a production-like environment for testing. And it's just painful to run those tests. I don't know.

JESSICA: Yeah, it is. And I won't run them nearly as often. But the adapter hardly ever changes. And really, what I care about with that adapter is that the other service is still doing what the adapter expects.

 

This article has some discussion of fakes blog-post-how-i-write-tests

Related to testing-concept-programmable-fake

Referring Pages

blog-post-replacing-mocks-with-hand-written-test-doubles