TDD Process Smells


This list of "process smells" focuses on execution of the practice of test-driven development (TDD)--not on what the individual tests look like. There are no doubt dozens of similar smells; following the rule of 7 (+/- 2), I've chosen the smells I see most frequently.
  • Using code coverage as a goal. If you practice test-driven development, you should be getting close to 100% coverage on new code without even looking at a coverage tool. Existing code, that's another story. How do we shape up a system with low coverage? Insisting solely on a coverage number can lead to a worse situation: Coverage comes up quickly by virtue of lots of poorly-factored tests; changes to the system break lots of tests simultaneously; some tests remain broken, destroying most of the real value in having an automated test suite.
  • No green bar in the last ~10 minutes. One of the more common mis-interpretations of TDD is around test size. The goal is to take the shortest step that will generate actionable feedback. Average cycle times of ten minutes or more suggest that you're not learning what it takes to incrementally grow a solution. If you do hit ten minutes, learn to stop, revert to the last green bar, and start over, taking smaller steps.
  • Not failing first. Observing negative feedback affirms that any assumptions you've made are correct. One of the best ways to waste time is skip getting red bars with each TDD cycle. I've encountered numerous cases where developers ran tests under a continual green bar, yet meanwhile their code was absolutely broken. Sometimes it's as dumb as running tests against the wrong thing in Eclipse.
  • Not spending comparable amounts of time on refactoring step. If you spend five minutes on writing production code, you should spend several minutes refactoring. Even if your changes are "perfect," take the opportunity to look at the periphery and clean up a couple other things.
  • Skipping something too easy (or too hard) to test. "That's just a simple getter, never mind." Or, "that's an extremely difficult algorithm, I have no idea how to test it, I'll just give up." Simple things often mask problems; maybe that's not just a "simple getter" but a flawed attempt at lazy initialization. And difficult code is often where most of the problems really are; what value is there in only testing the things that are easy to test? Changes are most costly in complex areas; we look for tests to clamp down on the system and help keep its maintenance costs reasonable.
  • Organizing tests around methods, not behavior. This is a rampant problem with developers first practicing TDD. They'll write a single testForSomeMethod, provide a bit of context, and assert something. Later they'll add to that same test code that represents calling someMethod with different data. Of course a comment will explain the new circumstance. This introduces risk of unintentional dependencies between the cases; it also makes things harder to understand and maintain.
  • Not writing the tests first! By definition, that's not TDD, yet novice practitioners easily revert to the old habit of writing production code without a failing test. So what if they do? Take a look at Why TAD Sucks for some reasons why you want to write tests first.

3 comments:

  1. I think it's important for you to emphasize that TDD is primarily about design, and the 'smells' we should be looking out for are the ones which indicate the design process is being ignored or undermined.

    Sure, we utilize familiar unit testing techniques and tools, but the goal really has little or nothing to do with unit testing or associated code coverage levels.

    If you're not breaking your TDD tests regularly, then it's likely you've not applying the Red-Green-Refactor motto.

    Red = prove the test *can* actually fail
    Green = simplest implementation to pass the test
    Refactor = infuse quality by applying SOLID design principles

    I cannot stress strongly enough how significant quality infusion is to sustainability. Without it, the application will become rigid and fragile over time, and velocity will inevitably slow as complexity increases.

    Remember: Red-Green-REFACTOR! :-)

    ReplyDelete
  2. Rightfully so, but we only have one little 3x5 space on the card. Your comments belong in the commentary though. Not going red is a great hindrance.

    ReplyDelete
  3. Thanks for interesting article.
    I've translated your article in russian http://sly-and-fluffy.blogspot.com/2011/10/tdd.html

    ReplyDelete

Note: Only a member of this blog may post a comment.