If you’re building anything more complicated than, oh, a simple “Hello World!” app, odds are that you’re going to end up with dependencies (•). These dependencies can vary all over the place, from access to system resources (How d’you think the “Hello World!” actually got to your screen?), other components in your system, external resources, outside applications, and so much more. What’s worse is that
- the dependencies might be under development too
- even if they’re stable, you might not have everything necessary to access them (e.g., the auth framework to get at S3)
- and even then, you might want to test specific responses that you can’t guarantee (it needs to always fail. Or succeed. Or randomly based on a PRNG)
And that’s where mocking comes in, where you essentially fake your interactions with the dependencies, so that you can go about implementing your stuff without depending on, well, your dependencies.
Which is all well and good, and actually kind of intuitive when you get down to it, with one caveat — you’re likely to hear the terms Mocks, Stubs, and Shims used somewhat interchangeably when you get around to googling.
Me, I tend to stick with the following semantics. You may or may not choose to use these, but whatever you go with, be sure to make sure that you have agreement with everybody else on your team. Also, document it for the sake of future team members, as well as future you!.
(Or heck, you may go down the “Mocks are a Code Smell, #JustSayNo” approach to existence. Just as long as a you have a consistent definition of what you mean by “Mock” 😆)
Mocks: A full-on stand in for the dependency, that replicates most or all of the behavior that you actually give a s**t about. These can actually get somewhat hairy (hence the “Code Smell” crack above), since, in extremis, you basically need to track/replicate the functionality of the dependency. Then again, if you can get a good mocking setup in place, it can be truly invaluable.
The key, however, is that you interact with the mock in exactly the same way that you interact with the dependency, via APIs, interfaces, messages, or whatever.
Stubs: These are extremely simplified versions of Mocks, which typically just return a set of hard-coded values, and/or fail, again, in simplistic ways. Think of the Stubs ←→ Mocks spectrum as one going from minimal functionality to full functionality, as shown above. You interact with the stub exactly the same way as you interact with the dependency (APIs, interfaces, etc.).
Stubs are particularly useful for testing out your interfaces to the dependency, as just getting things going (hard coded responses are better than no responses!)
Shims: Sometimes you just don’t have the ability to create a Stub or a Mock, either due to to time constraints, complexity, or just lack of resources. In those cases, you might end up sliding in some code that basically acts as if you are interacting with the dependency.
The key with Shims though is that you don’t use the interfaces — APIs, messages, etc. — that you usually would to get at the resource. For example, if you needed to retrieve something from S3, you just bypass the entire S3 interface, and just grab the object from a local file.
Shims, in my experience, have been responsible for more chaos than I can shake a stick at. They create detours in your code, detours that can lull you into a false sense of security, or worse, detours that you forget you had in there. Use them with care, and only when you absolutely need to (and yes, there are times when you will!)
So there you have it — Mocks vs. Stubs vs. Shims!