Thu 6 Oct 2011
I’m a Lazy Tester
Why, then, do I feel so strongly that a team should spend so much time and effort writing and maintaining a unit test suite that will never be part of the application?
I hate puzzling through crappy code. I hate debugging why a seemingly insignificant change causes the application to fall over. I hate small functional changes that take weeks to deliver. When I get off my ass to do something, I want to focus on it, get it done, and go get a beer. And I don’t even want to think about interrupting beer time to go back and fix something that I thought was done.
So…how can unit tests help me be lazy? <sips beer> My prerequisites for being lazy are:
- If I break something, I know right away that I broke it
- If I break something, I know something about why it is broken
- The production code is easy to understand
- The production code is easy to change
Whoa, whoa now! It’s fairly obvious how points 1 & 2 might be achieved via a strong unit test suite. Good unit tests act as living documentation; a specification of the assumptions and expectations of your production code. If I make a change that violates the spec, a test will fail and I’ll know what the problem is. But the unit tests are not production code; how will my unit test suite make my production code easier to work with?
Points 3 & 4 are starting to bump against the idea of well-factored and/or clean code. (Clean code is one of those things that, like pornography, is hard to define but you know it when you see it. And everyone has a different opinion about what makes it good. But I think I mentioned I’m lazy; if you’re interested in a more detailed discussion, I suggest you consult the internets.)
Most teams I’ve worked on have some working agreements related to refactoring; we’d often intone a mantra such as ‘make it work, make it right, make it fast’ while coding. But it can be hard for a team to quantify when the code is ‘right.’ Code reviews (good) or pair programming (better) helps to achieve rightness; but even mature, rational developers can disagree about when code is clean enough. Code review often isn’t enough to maintain a clean code base.
So, how do unit tests make code clean? Well…they don’t, necessarily. It’s more accurate to say that strong working agreements around unit testing will drive the team to exhibit code-cleaning behavior.
- Test-Driven Development (TDD): if you want to make a change in code, you have to write a failing test specifying what the behavior should be.
- 100% coverage: If you change a line of code, at least one unit test will fail.
- ‘Grok’ test: coming in cold, an experienced developer can understand what the code does in a few seconds.
If you’re reading this, you’re probably someone who works with code. Take a minute and go find some code you maintain that you don’t think is ‘clean.’ Now, allow me to make some predictions:
- it has low or no unit test coverage
- what tests it has tend to have more lines of code doing ‘setup’ and tweaking input parameters than actual testing
- it takes a bit of time to understand what each test is actually testing
- it isn’t easy to predict which tests will fail (if any) if you tweak a line of code
- it isn’t easy to assert, with confidence, that all the assumptions and expectations of the code under test are covered by unit tests
Or, you could start cleaning that crappy code up. Separate some concerns, extract some classes and methods (testing them along the way, of course), rename some things to better describe what they do…in other words, refactoring. Not only will the code be easier to understand and easier to test, I bet you’ll actually have to write fewer tests to get coverage. Testing poorly factored code is actually more work than testing and refactoring.
