Disclaimer: This post is meant to entertain and maybe—just maybe—have you reflect on your relationship to software unit testing.
Developers who do not regularly write unit tests, don’t want to start writing unit tests. Probably due to inertia, the natural tendency to resist change. It seems too cumbersome. Once those same developers start writing tests, however, they quickly find, to their surprise, that they are in fact, enjoying it. Who would have thought?
There is a kind of gamification quality to unit testing. Seeing that green bar of passed tests tick up to a perfect score of 100% is not unlike the never-ending hunt for loot or experience points of the grinder games that attract so many people. It is, for some reason, appealing to us, to collect those points. Developers get pulled in—they get hooked.
Soon, they find themselves spending more time writing tests. They want—no they need —the code coverage at a steady 100%. So, they spend entire days on Stack Overflow to figure out how to trick their testing framework into believing that the private constructor of their utility class—which, in no way, shape or form, will ever be invoked in production—has in fact been covered by tests.
They test all of their code, even the myriad of trivial methods sprinkled across their codebase – they need to test everything, every branch must be covered.
At this point, they are just procrastinating through writing a plenitude of tests. They feel that it’s much easier to write tests than to get started on that next feature that their users need (for which, by the way, the requirements are way too vague anyway).
Just, one — more — line of code — covered...
We, developers, are all on this journey, towards testing enlightenment, and this is how it goes.
Chances are, that you, in the beginning, can’t be bothered to write tests at all. Sure, you, kind of, understand the point of them, but, it’s just not for you. It’s simply not worth the trouble. You have production code to attend to. The real code, that’s what matters, right? That’s what you are really being evaluated on, right? So, why spend time writing irrelevant unit-tests? Your test and QA-team picks up any stray bugs, later, right?
At this point, you are a unit-test ignorant. Some people stay like this their entire career. Nothing intrinsic to you can push you along. If everyone around is unit-testing ignorant, then that’s what they’ll stay, indefinitely. To advance, something, or someone, external is required. There needs to be something to disrupt the status quo.
At some point, you are forced to start writing unit tests. It might be a manager, a tech lead, a colleague, or possibly your own impression of ever-decreasing self-worth, that gets you started. You start writing tests mostly for the sake of writing tests.
If you run into code that you, in a minute or less, can’t figure out how to test, you quietly ignore it. “What the heck is the difference between a mock and a spy, anyway?”, you ask yourself. You test your getters and your setters, and that’s about it. You are now qualified for the epithet of unit-testing hesitant.
What happens next is as unimaginable and wonderful to you, as it is to everyone around you. All of a sudden, after having written a couple of hundreds of lines of test code, the process—heavens above—actually starts being pleasant. You realize that it’s not all that bad. You get a warm and fuzzy feeling, deep down in the gut of your programmer-soul, when you see that green bar slowly crawl up to 100% (or is the colour of your test suit better described as emerald? Emerald sure sounds more precious, doesn’t it? And precious is just what your test suit is, right?).
All tests pass. Sure, every once in awhile, you might need to have that guru on your team describe to you how to capture the parameters passed to your mock. But, you are definitely on the right track. You tell yourself.
As you start convincing others to write tests, you have now transitioned into being a unit-testing enthusiast, good for you!
Before you know it, you are that guru. You know all there is to know about mocks and fakes, TDD and BDD. Your non-programmer-friends thinks you have become interested in pickling vegetables, like all the other hipsters, because of your constant ramblings about Gherkin. The code you write is absolutely, one hundred percent, testable. Dependency injection is second nature to you—and non-negotiable. Your code can effortlessly be injected, stubbed, faked, spied, mocked or what have you. Not a flaky test in sight.
If you cannot test it, then no one can. The tests themselves have become more important to you than anything else. More important to you than your production code and, with that, more important than your users and the business itself. Hours are spent on online forums to find out how to trick your testing framework into giving you full coverage. You book your managers for a sit-down, to tell them about the cost-effectiveness of unit tests, and how “it’s of utmost importance that we never, ever, for no reason, what-so-ever, allow a single line of code to be committed without it being adequately covered by tests “.
You are, at this point, effectively procrastinating by writing tests. Also, what’s worse, you have turned into a testing fanatic.
Some people make it to the zenith of unit testing, the very last stage of our journey. Mind you, very few make it here because it is easy to get stuck somewhere along the way. Just like at the AA, the first step towards recovery is to, genuinely, admit to yourself, that you have a problem. Your overambitious testing is your problem. In the extreme case, co-workers tire of test fanatic’s zealousness and organize an intervention—oh, how awkward!.
Only after realizing the problem, might you reach the Zen of testing and become a unit testing pragmatist. Testing pragmatism, testing enlightenment, reaching unit testing insight, nirvana, what have you. At this point, your testing journey has come full circle, returning to where you started; at the notion that delivering features and production code is your main objective.
Unit testing is a mere tool to get features out the door more quickly and with fewer bugs. It is for you to feel reassured that your code does what you believe it should do, it is not for proving anything to anyone else—and certainly not top-out some, seemingly arbitrary, metric or KPI. Your tests are there to future-proof and to document your solutions.
You test the complex, non-trivial, business-critical code and leave the getters and setters alone. You test integrations and interfaces with third-party code, edge cases, code that might have recently changed, or code that will likely change in the future.
Your bastard private constructors are no longer keeping you awake at night. You lead a more balanced, happy-go-lucky, and more content life as a programmer—to the delight of your co-workers and the satisfaction of your clients and customers.
I am also on this journey, and I find myself somewhere between being an enthusiast and a fanatic. I am working on improving. Oh, boy, am I working on improving. In many ways it’s similar to the Zen Buddhist monks’ search for enlightenment: the harder one tries, the less likely one is to get there.
How far along have you come?