In this series so far, I’ve introduced the concept of unit testing, talked about how to avoid early failures and frustrations, and then dived more in depth into the subject. But now that you’re familiar with the topic and reasonably well versed in it (particularly if you’ve been practicing over the course of this series), I’d like to use that familiarity to discuss why unit testing makes sense in terms that you now better understand. And beyond that, I’d like to discuss how you can use this sense to overcome inertia and even objections that others may have.
The Case for Unit Tests
Alright, so this is the part where I offer you a laundry list, right? This is where I say that unit tests improve code quality, document your code, exercise your API, promote (or at least correlate with) good design, help you break problems into manageable chunks, expose problems earlier when finding them is cheaper, and probably some other things that I’m forgetting right now. And I believe that all of these things are true. But after a number of years of faithfully writing unit tests and practicing test-driven development (TDD), I think that I can offer those as appetizers or footnotes in the face of the real benefit: they allow you to refactor your code without fear or hesitation.
First to Contend With: Inertia
Inertia could probably be categorized as an objection, but I’m going to treat it separately since it manifests in a different way. Objections to doing something are essentially counterarguments to it. They may be lame counterarguments or excellent counterarguments, but either way, they take an active position. Inertia doesn’t. It’s either ambivalence or passive-aggressive resistance. To illustrate the difference, consider the following exchanges:
Alice: We should start trying to get our code under test.
Bob: Unit testing is stupid and a complete waste of time.
Alice: We should start trying to get our code under test.
Bob: Yeah, that’d be nice. We should do that at some point.
The former is a strident (and obtuse) counterargument while the latter is an example of inactivity by inertia. In the second exchange, Bob either thinks unit testing is a good idea — but just not now — or he’s blowing sunshine at Alice while subtly saying, “not now,” so that she’ll leave him alone and stop stumping for change (i.e. the passive-aggressive approach).
In either case, the best way to overcome inertia is to counteract it with activity of your own. Inertia is the byproduct of the developer (and human in general) tendency to get stuck in a rut of doing the comfortable and familiar, so overcoming it within your group is usually just a matter of creating a new rut for them. This isn’t necessarily an easy thing to do. You’ll have to write the tests, make sure they stay up to date, demonstrate the benefits to anyone who will listen, and probably spend some time teaching others how to do it. But if you persevere and your only obstacle is inertia, sooner or later test writing will become the new normal and you’ll get there.
Red Herrings and Stupid Objections
Around a year ago, I blogged about a guy who made a silly claim that he wrote a lot of unit tests but didn’t check them in. The reasoning behind this, as detailed in the post, was completely fatuous. But that’s to be expected since the purpose of this claim wasn’t to explain an a calculated approach but rather to cover a lack of knowledge — the knowledge of how to write unit tests.
This sort of posturing is the province of threatened Expert Beginners. It’s the kind of thing that happens when the guy in charge understands that unit testing is widely considered to be table stakes for software development professionalism but has no idea how it works. As such, he believes that he has to come up with a rationale for why he’s never bothered to learn how to do it, but he’s handicapped in inventing an explanation that makes sense by virtue of the fact that he has no idea what he’s talking about. This results in statements like the following:
- Unit tests prevent you from adapting to new requirements.
- Testing takes too much time.
- It wouldn’t work with our style of writing code.
- You’re not going to catch every bug, so why bother?
- Writing all of your tests before writing any code is dumb.
- Management/customers wouldn’t like it and wouldn’t want to pay for it.
I obviously can’t cover all such possible statements, but use the smell test. If it sounds incredible or stupid, it probably is, and you’re likely dealing with someone who is interested in preserving his alpha status more than creating good work product. To be frank, if you’re in an environment like that, you’re probably best off practicing your craft on the sly. You can write tests and keep quiet about it or even keep them in your own personal source control (I have done both at times in my career when in this situation) to prevent people from impeding your own career development. But the best longer term strategy is to keep your eyes and ears open for other projects to work on where you have more latitude to set policies. Do an open source project in your spare time, grab an opportunity to develop a one-off tool for your group, or maybe even consider looking for a job at an organization a little more up to speed with current software development practices. You can stay and fight the good fight, but I don’t recommend it in the long run. It’ll wear you down, and you’re unlikely to win many arguments with people that don’t let a lack of knowledge stop them from claiming expertise on a subject.
I Don’t Know How to Unit Test
With inertia and silliness set aside, let’s move on to legitimate objections to the practice. At first blush, you may be inclined to scoff at the objection, “I don’t understand it,” particularly in an industry often dominated by people unwilling to concede that their knowledge is lacking in the slightest bit in any way whatsoever. But don’t scoff — this is a perfectly reasonable objection. It’s hard and often unwise simply to start doing something with professional stakes when you don’t know what you’re doing.
If the people around you admit to not knowing how to do it, this earnest assessment often indicates at least some willingness to learn. This is great news and something you can work with. Start teaching yourself how to do it so that you can help others. Watch Pluralsight videos and show them to your coworkers as well. If your group is amenable to it, you can even set aside some time to practice as a group or to bring in consultants to get you off to a good start. This is an objection that can easily be turned into an asset.
It Doesn’t Work With the Way I Code
I originally specked out this post in the series because of a comment in the very first post, and this section is the one that addresses that comment. Arguments of this form are ones I’ve heard quite frequently over the years, and the particulars of the coding style in question vary, but the common thread is the idea that unit testing creates friction with an established routine. This isn’t the same as “I don’t know how to unit test” because the people who say this generally do know how to write tests — they must or else they wouldn’t know anything about the subject and would make up Expert-Beginner-style stupid excuses. It also isn’t the same as the inertia objection because they’re saying, “I was willing to try, but I find that this impedes me,” rather than, “meh, I dunno, I like my routine.”
My short answer to someone who has this objection is, to put it bluntly, “change the way you code.” Whatever the specifics of your approach, when you’re done, you don’t wind up with a fast-executing safety net of tests that you trust — tests that document your intentions, keep your code flexible, help prevent regressions, and force your design to be easy to use and decoupled. People who code differently than you do, in that they unit test, do wind up with those things. So figure out a way to be one of those people.
On a deeper level, though, I understand this objection because it hits closer to home. I was never the type to bluster like an Expert Beginner, nor am I prone in the slightest to inertia. (I am pretty much the opposite — frequently biting of more than I can chew.) The other objections never really applied to me, but this one did both prior to starting to write tests and prior to adopting TDD as my exclusive approach to developing. You can read that latter perspective from my very early days of blogging. Years ago, I chafed at the prospect of unit testing because spending the extra time took me out of the ‘flow’ of my coding, and I balked at TDD because I thought “why would I start writing unit tests for this code when it might be refactored completely later?” In other words, neither one of these worked with my approach.
But in both cases, I relented eventually and changed the way I coded. I didn’t just one day say, “well, I guess I’ll just start writing code differently from now on.” What happened instead was that I realized that a lot of really bright people and prominent names in the industry had coding and design styles that were compatibly with writing tests so it was at least worth trying things their way. It wasn’t a matter of doing something because the cool kids were doing it or resolving to change my ways. Rather, I thought to myself, “I’ll see what all the fuss is about. Then, I’ll either like it or go back to my way of doing things armed with much better arguments as to why my way is better.” So I poured myself into a different way of doing things, forced myself to keep at it even though it was slow and awkward, and, wouldn’t you know it, I eventually came to prefer it.
Convincing people in your group to follow that lead is not going to be easy, but it is doable. The best way to do it is to earn their respect and show them results. If your code is cleaner and freer of bugs and your past projects are easy to adapt and modify, people are going to notice and ask you what your secret is, and you’ll be in a position to show them. It may seem improbable to you now, but you can go from being a person quietly teaching yourself to unit test on the side to one of the key leaders in a software department relatively quickly. You just have to commit yourself to continuous improvement and moving toward proven, effective techniques. Unit testing is just one such technique, but it is a powerful and important one.