Part of 2 in the series
Yesterday, I started my explanation about not using automockers. But that muse is about just one aspect of the automocking world: interaction tests. And automockers can do a lot more than that.
We could use an automocker for years without ever writing an interaction test. So, then, again, why don’t I? I’m an old fart, ya know. To the extent I’ve learned anything at all in my time here, I’ve learned that every strength is a weakness. I see that in myself, I see that in code, I see that in technique, I see it everywhere.
A casual f’rinstance, the thing that makes me good at speaking is the same thing that makes me speak too much…and a million other cases. The great strength of the modern automockers is their ability to "point and shoot". To fake a class for a test, you literally point the automocker at the class and start shooting fake responses into it. And current automockers can point at anything and shoot anything. An aside: If you want to really master your language’s internals, master your automocker’s code. How they work is quite brilliant.
So if that’s the strength, what’s the weakness? Well it’s exactly that. An automocker can point and shoot at anything. The upside: It can point at jdk or library classes. It can point at interfaces. It can point at sealed classes. The downside: It allows me to live in a forest full of terrible designs. I’d go so far as to say it even encourages me to do so. When I use an automocker to get out of a nasty design — to work around an awkward collaborator — I keep the awkward collaborator. If I automock, say, java’s jdbc library in a database-centric app, I am going to be living with that library infesting my code forever.
For the as-yet-unexposed, the jdbc library forced me to coin a new group-word. You know, a pride of lions, a litter of puppies. Jdbc is an asshattery of awkward collaborators. A typical jdbc class has 100+ api’s, is output-only, is interface-only, and has zero support for testability.
If I use, say, resultset, freely throughout my code base, I will never ever be able to test that base without either automocking or a live db. Riddling my code with resultset is virtually guaranteed to leave me in pain. I don’t like pain. And that sounds like a joke, but it’s not. It’s actually a mantra. If more geeks eliminated pain instead of tolerating it, geekery would be a better place.
Now, automocking resultset is certainly better than writing thousands of lines of db setup code for my tests. Freely granted. But there’s a better way. The answer is a little too detailed to go into, but I can telegraph it pretty quickly. Write tiny custom faces that wrap around resultset (or don’t, hang on) and show the world just the custom api needed for a given client. Instead of resultset letting me grab fields from a query producing order rows, I have an orderresult that counts and returns order objects. Far from having 100+ api’s, I have (at most in my experience) 4. And that guy is easy to fake. In fact, typically, I don’t fake him at all. I give him a constructor that takes order… or some such. And I’m perfectly content to riddle my code with orderresult objects, cuz they’re not awkward collaborators. This is all about relative ease. Hardest: Use live db results. Easier: Use an automocker. Easiest: Improve my design.
And I want to be clear, here. I could have seen that going in. I could be judicious in my use of automocking. This is all slippery-slope stuff. It’s about the feels, the habits, the mindset. If I were a knows-in-advance type, I’d’ve just known. Automockers certainly don’t force me to make bad decisions. I could be a guy with 89 rules for design. But it’s easier to remember just the one rule: When the code hurts me, change the code. I rarely use an automocker because I have found that the habit of using them lets me walk away from a problem without solving it. It’s like I’m purposely resisting using a perfectly fine tool, because when I use that tool, I don’t have the discipline to use it wisely. That may seem like a weakness in me. I’m okay with that. If every strength is a weakness, every weakness is a strength, too.
So. When I fake, in non-legacy situations, 99% of the time I handroll the fake. If I don’t like doing that, I change the design. I do this because, watching myself and watching whole teams, over the years, it’s just too easy to go wrong. And it’s slippery. It’s fine here. Less fine over there. Not good in that place. And really bad in that other place. Gradual collapse. The first time you see a 100-line automock setup for a test, you’ll feel me on this. I think that’s it. Maybe, tho, before we leave, the two cases where I do use automockers. First, remember the orderresult <-> resultset collaboration? If that’s at all intricate, which it might be if the protocol is implicit well for just that one collaboration, I might have to pull out my mockito.
Having isolated my main base from the shite, I feel no resistance to automocking, tho I do first see if I can simplify the collaboration. And the second? Well. Legacy. I travel all over the world tinkering with code that is riddled with awkwardness. If I need pindown tests, the automocker can be a very quick way to get there. Pass in the dependency, roll an automock, test the branch.
But even here, my mission is to test the bad design just enough to change it. I’m not walking away after I do that, but am mid-stride.
My advice: Use automockers sparingly if at all. Respond to testing pain by changing designs. Study yourself.