a little madness

A man needs a little madness, or else he never dares cut the rope and be free -Nikos Kazantzakis


Archive for March, 2007

Testing Is … Good For Your Design

Fear not, despite the title I’m not going to start pontificating about design theory or TDD. Like always, the “Testing Is …” series sticks to straightforward ways in which tests make our lives easier. Being the lazy programmer that I am, I’m always looking for ways to make writing test cases easier. I suppose you might call this “designing for testability”.

Certain coding practices lead to code that is difficult to test: at best they make writing the tests painful, at worst they discourage testing and lead to maintenance pain down the track. The good news is that many of these practices are a sign of bad design in general, so by keeping testability in mind while you code you will often end up with a cleaner design. I look at it as the tests being a client of my code that help to keep the design honest.

An example is probably in order. First off, I find that it is unit tests that really drive the improved design. In unit tests we try to isolate a distinct piece (or “unit”) of code so that it can tested independently. The better we can isolate the units, the better we can cover the code as a whole without a combinatorial explosion in the possible inputs and outputs. The great thing is that well isolated units exhibit a key design practice: Separation of Concerns. To be truly isolated, a unit should be concerned with only one task. A complex system is much more powerful if it is built out of small, distinct units that can be combined in interesting ways (the “Unix way”).

A common counter-example that suggests testability and good design are actually competing goals is the apparent need to access internal unit state in tests. The problem with this of course is allowing the test to access that state is at odds with another key design practice: Encapsulation. In practice, I actually find that the need to access internal state is exceedingly rare. I would go so far as to say I consider it a design smell: if you feel the need to assert over some internal state in your tests, there is a good chance that the unit granularity is wrong (perhaps you have not truly separated concerns). There are exceptions of course, but in most cases refactoring is a better solution to subverting encapsulation. Thus, rather than countering, this example enforces that having test code as a client drives improvements to design.