Testing is important, let’s for a moment agree on that. Untested code might more likely break, now or at some point in the future. If I say “might”, then Murpy immediately corrects me and tells me it “will”. Definitely. Even if we manage to write perfect code right now, it’s still nice to have regression tests and be much more confident and agile for future changes and refactorings. Also, tests are always some kind of a proof that we tried our best to produce a good product, for us, for the team, for our boss, and, wait for it, for our customers. After all, if code breaks, every argument is obsolete, because no matter what, code that breaks is always wrong.
Besides my all-time favorite that there is no time to test, I also hear the following again and again: “We can’t really test that, it’s just not, um, testable!” Be it the Java application that doesn’t allow dependent services to be replaced by mock implementations. Be it that we’re hard-coding configurations in a way that we can’t switch to test configurations. Or be it that our web application can’t be easily tested because we don’t have ID’s in our HTML elements. Yes, that’s all legitimate reasons why code isn’t easily testable. But is it also legitimate not to test because of those reasons?
In short: It’s not.
If code isn’t (easily) testable, we have a bunch of options: we don’t write tests and ignore what we said earlier in this post. We test it in another context, maybe on an integration test level (for example). We test it manually, which is often an option, but then we’re missing out on all the advantages regarding repeatability, usage as regression tests, and continuous integration. Or, and that’s the point of this post, we’re changing our code to make it testable. Not always, but often that means introducing something that doesn’t purely serve functionality but is additionally introduced in order to make it testable. We might end up with 5 lines of code or even with an indirection more that we wouldn’t need otherwise. At least we can test it now. Or, even better, we think about testability from the very beginning (oh, and: test first, anyone?) and aligning design and implementation accordingly. Very quickly we will recognize untestable code, untestable patterns, and untestable designs at first sight and we’ll make slight but important changes. With best regards to our test coverage.
Don’t hesitate to make changes to your code to increase testability. After all, testability is also a design goal.
EDITED: looking back the few months since I’ve written this post I made more experiences manifesting testability and a very important yet mostly underestimated goal in software development. Today I stumbled upon a presentation by Michael Feathers of Object Mentor talking about the Deep Synergy between Testability and Good Design. I realize how much more further he goes with his thoughts, and how much more experience has to speak out of. 100% agreed, Michael, nice presentation!