Saturday, May 22, 2010

No value in unit testing simple classes?

I recently read a blog entry by Steve Sanderson regarding selective unit testing.  I really appreciated both the candid treatment he gave the topic and the subsequent comments.  It really made me think about the habit I have developed of using TDD to develop everything (almost).  In fact, it is the "almost" that I strive to remedy.  I feel like I wrote stinky code when I have developed something that couldn't be TDD'd.  Some .NET framework classes are really hard to mock (no interface) or have so many dependencies that I simply can't bring myself to spend that much effort.  But, I digress.

One thing struck me about Steve's comments that I simply had to explore.  He stated, that for "trivial code with few dependencies...it doesn’t matter whether you unit test it or not."  He doesn't stress this position.  In fact, he states that he is perfectly OK with those of us who take the time to write tests for trivial code. 
However, I still feel these tests offer significant value to the code base and to the developers who must work with it.  First, writing these tests benefits the developer:

  1. TDD is a practice which focuses the thinking on creating the simplest solution that can work one step at a time.  It helps me think out of the box and sometimes produces an even simpler solution than the one I started to jump to.  Eventually, I can break down harder problems because I have so much practice at it with easy problems.  As Kent Beck says in his TDD book, you should always be capable of moving at a slow pace when necessary.  TDD of a trivial problem is a kata!
  2. Existing tests can serve as a template for a new test to drive a design change or a defect correction.  Call me lazy, but when the previous dev already has gone to the trouble to create all of the test setup, mocking, etc. it can save me time when correcting a defect or extending a behavior.  Yes, these happen even in classes I considered to be trivial.
These tests offer immediate and long term value to the maintenance of the code base:

  1. I am firmly convinced that trivial code often becomes more complicated (perhaps over-complicated if we don't watch).  For many systems I've worked on,it happens sooner than I think it will.  It is worth the small effort to write the tests to assist in the later refactoring.  Why not do it while the context of the original required behavior is fresh in your mind?
    • Yes, you could add tests after realizing they are needed. But that doesn't produce the same kind of tests and ignores the Red-Green-Refactor design cycle which I contend still has value for trivial problems.
    • Have you ever discovered that your "simple" class actually has too many responsibilities?  It is so much easier to refactor into multiple classes with tests on the behaviors. 
  2. Even if refactoring never happens:  My idea of trivial may not be the next dev's idea of trivial. Tests help that person understand what behavior I intended for the method as well as which edge cases I considered.
    • Yes, the source code expresses the actual behavior, but tests developed as part of TDD record what the developer thought the behavior was supposed to be. 
Finally, every piece of code I write starts off being simple (ie trivial) and eventually evolves into something more complicated.  I simply can't see myself waiting until a class reaches some arbitrary level of complexity before deciding to start writing tests.