The Side Benefits of Comprehensive Unit Tests

I feel like I’ve written this article before but I can’t find it if it I did so here it goes…

I totally have a bee in my bonnet about unit tests. Lots of them. I managed a team of engineers in my previous role on Microsoft Edge and I was a real zealot about it then. I had a pretty simple approach to this summarized in my most quotable quote:

“There’s only one coverage number I’m interested in: 100%”

Why would I say this? Keeping in mind that I know these things can be gamed and there’s many ways to measure (e.g. lines, blocks, branches). But 100%? Why?

Well it’s simple.

  1. I’m so tired of the excuses. “This is hard to test,” “I’m sure there’s no bugs”, it goes on and on. There is no code that can’t be unit tested with a little work; I wrote about some drastic ways you can do it even in the presence of horrible globals and data-types that don’t exist where the test will run. I think you can do better than the drastic choices but the fact is anything can be tested and most things should be tested. By targeting 100% you’re saying there will be zero time devoted to excuses for this code or that.
  2. If you know you have to test all of your code then you will write it differently. Better. You will think about dependencies: you will want to avoid taking them. You will not copy-pasta your code because you do not want to write new tests for the pasta. You will not use wonky global side-effecting methods or techniques because you can’t observe them or create a suitable test environment for them. In short, you will avoid many a plague and end up with better code.
  3. If you have existing code that is not using good patterns you will be highly motivated to make it better because otherwise you are going to have to find a way to test the crappy code.
  4. You will begin to find that the incidence of bugs in code that you never tested is remarkably high and, by getting those bugs out, your productivity will increase. You will be able to make major changes with confidence.

Of course it’s possible to achieve this coverage result with “bad tests” but even then you will reap many of the collateral benefits of having written testable code. You will spend time maintaining those tests, and if they are too coupled, it may drive you crazy sometimes, but the alternative is far worse. Many, many, bugs can be found simply by running the code, at all, in any configuration.

Somehow my team managed to get to 100% in many areas (though not universally). Somehow we managed to test things that had traditionally had no tests, or been deemed “untestable.” Somehow we managed to achieve amazing overall productivity and keep high esprit de corps even while having very tricky code to maintain.

It’s almost like shooting for 100% makes you better.

Who’da thunk.