posted on Tuesday, April 04, 2006 10:29 PM by johnwood

The Case for Unit Testing

Leaving aside whether you're for, against or just don't get test driven development, it's worth noting that unit testing will always be necessary to write reliable code. It may seem obvious to some, I'd like to point out the reasons why unit testing is required even for those who diss or dismiss test driven development.

For one, programmers like to re-use code. It doesn't matter whether that code is domain-specific or low level, re-use happens. It clearly helps productivity. However every time you share code with another module or class or method, you're opening your code up to changes made to that shared code: The code can certainly change behind your back.

When you invoke that shared code, you're just passing in parameters, you're not specifying what you expect to happen. The method name might give some hint, but other than that there's really no indication of what you're intending that method to do. The method might be called CalculateCost but for all you know it's planting daisies and you won't see any compilation errors. It's free to do whatever it likes, and that makes your code less reliable. High level, low-level, structured or spaghetti, any code that utilizes re-use is brittle.

Like it or not you're also going to be adding bits to code as time goes on, and anything you change has the potential to inadvertently break existing dependencies or existing behavior.

Not to mention that people make mistakes. Code may change accidentally, not re-used code but the primary code itself. With an infant in the house sometimes I find random blurbs of keystrokes in my code. Luckily this breaks in compilation, but what if she just inserted a 0 inside a literal number.  Or a semicolon in the wrong place. Source control helps here, but unless it's highly guarded and nothing can be checked in without review, then you're bound to accidentally and inadvertently change code at some point.

So what can we do about it?

One solution is to never change the code. Once you know it works 100%, it gets set in stone. The code isn't allowed to change and neither are any of its dependencies. A bit like compiling to an executable and throwing away the source code.

Unfortunately this solution just doesn't work in the real world where requirements change and upgrades happen. At some point you will just have to change that code.

Another option is not to re-use any code, to write everything very clearly and have tons of peer reviews before code is checked in, and to pray every night. This also isn't realistic or infallible.

There really aren't any other solutions other than unit testing.

Unit testing involves creating a set of independent executable tests that validate the behavior of every piece of code you write. The test becomes a kind of contract for the code's behavior. If you inadvertently or intentionally change some shared code and break existing behavior, well you'll know about it because - fingers crossed - the tests will fail. The tests can be written before or after the code is created, whichever works for the development process you adopt.

Maybe one day we'll find another solution that involves less work.  Until then you should make friends with nUnit or mbUnit (or the unit test features of VS2005). It's really the only way to write reliable code.

Comments