Beyond Mock Objects - The Code Whisperer

I understand what you mean, Darren, but I strongly disagree with the way you've worded "(Ruby's) flexible nature makes "injection" of dependencies a non-issue." I think this conflates two ideas.

Yes, with Ruby/Python/Smalltalk, we can easily substitute a test double at runtime for anything anywhere anytime. I see the benefit here, especially when introducing seams to pull things apart. This puts less pressure on the programmer to build tiny things up front, because pulling big things apart requires less gymnastics than say Java or C# does.

On the other hand, I can only easily stub Time.now() *everywhere*, so I need to do this carefully. This means that we can have the singleton testing problem (I have to remember to reset the singleton to its original state at the end of my test) everywhere, all the time. Great power, great responsibility, all that.

You write, "I mean, gosh... clear your mind for a couple minutes. Then imagine what we want to do, which is.... Get the current time." In the process of refactoring, I discovered something: I don't want to get the current time, because the current time binds me to a specific context. This makes the code less easily reused. So far, this doesn't sound like much of a problem, because I'm never going to reuse "find all customers with recent, open orders", right?

OK, but what happens when I remove one piece of the context. Now I have "find all customers with order orders close to any given date". This sounds like needless complication. But wait, there's more. I notice that by extracting one tiny thing, I can have "timestamp this request", and I can have it in only one place, and in the place it belongs--before any controller handles the request--and for any request that I will ever need timestamped. For logging. For audit trails. For booking reservations. For... gosh, I don't know for what, but for *everything*. (And, by the way, in every app I ever build with this request handling technology. Ever.)

If I keep now() buried deep in my code, then I probably don't see this. I know that, because I've kept now() buried deep in my code and not seen this. I've also seen dozens of code bases in which they're kept now() buried deep in their code and not seen this.

And that's the point.

You mentioned TimeCop on twitter: a magic, implicit abstraction for the various ways Ruby implements the concept of time. I've seen this before. It's called MockEJB and did exactly the same for the JNDI context in enterprise Java applications. It solved the same problem: I can uniformly hijack an entire concept and control it. But why do we need it at all? We need it because the original idea has no useful, unifying abstraction. This leaves me interpreting your attitude as this:

"Why abstract something that's poorly abstracted? Just keep not abstracting it. It'll be fine." This is an instance of conflating simple and easy.

Maybe. In some situations, yes. Under some extreme conditions, absolutely. But we miss out on learning things when we choose this path. We almost certainly miss out on the chance to improve things. Throwing good design after bad... I can't countenance that as standard operating procedure.

I just can't.