Api drift is so embarrassing!
Use dependency injection. Have minimal and fast collaborators with no side effects.
... leading, inexorably, to Functional Core, Imperative Shell
testing-concept-verifying-doubles
A hunk from the Ruby Rogues episode on Therapeutic Refactoring
CHUCK: Yup and I also, I really like too that you could immediately get a feel for what was going on in there. I mean just by looking at that stub, you could see, "Okay. Well, it's using all of these information or all of these methods on whatever's being passed in which is the target."
KATRINA: Right and one of the things that I didn't do in the talk but I did in the real code is that I defined some shared examples that just define that API, the accepted messages and then I run the map against the stub and against the original object.
JAMES: That's a good trick. I just learned yesterday from Sandi Metz, in fact.
KATRINA: That's where I learned it as well.
JAMES: Yeah, showing how, basically, people eventually complain that you mock the… mock or stub the API and then what happens is if you changed the API, your tests all passed, right? Because your mock still has that old API but it's passing in so it still passes whereas you don't get failure. So if you throw it in the module and then put that module and the tests against the actual class and also against the test double itself, then, you can verify that something will break if the API changes. It's very cool.
CHUCK: As we learned last week, that's also why you have acceptance test. It's so that it's running all the way through front to back and then if API does change and you mock doesn't, then your unit test will still pass and then you'll see that there's something that doesn't add up in your acceptance test.
JOSH: Yeah. Slight little tangent here, the RR, the test double library from Brian Takita. I like RR or Double R. It's very thirst in its syntax, which I find pretty nice. But one of the features that it supports is that it will, you know if you're doing a mock, it will… you can give it, you know if you're mocking a particular object and wrapping an object, it will actually check to make sure that the object itself also understands the method that you're mocking out. So that you're not just like stubbing something without checking to make sure that the thing that you're stubbing is there too.
JAMES: That's kind of neat. S basically if you try to define a diameter method but the underlying object doesn't have a diameter method, it breaks?
JOSH: Yes. Yeah, that's what you can do.
JAMES: That's cool.
CHUCK: Yeah. One other thing…
AVDI: Also is the RSpec-fire extension to RSpec.
CHUCK: RSpec-fire? What does that do?
AVDI: Ah. Something similar to that.
DAVID: It's like regular RSpec only it's the next model and it's in color.
JAMES: Plus it's on fire.
AVDI: It says in checking that you're mocking stuff that's actually three.
Possibly this article talks about the concept (I haven't read it yet) blog-post-how-to-write-future-proof-mocks-in-rspec3