When I started drafting this post, I was under the illusion that I had just stumbled across an amazing discovery, never before articulated by any programmers before me. But over the past few weeks, I have found out that what I am about to say isn't even slightly original. Everyone is saying it. But it took me quite some time to realize, so here it is anyway, for the two people out there who are as slow as me...
The concept of automated unit testing is appealing to most developers, even if the idea of writing all tests before writing code seems a bit over the top. It is certainly attractive to think that I might never have to perform another boring manual test of my software, because I can check it is working correctly simply by launching NUnit (or similar) and running through a suite of tests.
The trouble is, automated testing only works for a certain subset of classes - those whose outputs are clearly defined and easily measurable. And of course, those are exactly the type of classes I keep finding myself working on.
For example, I spent many months writing a library of custom Windows Forms controls. They had to be tested with keyboard and mouse entry, and at a variety of screen resolutions and font sizes. Unit testing was sadly out of the question...
Assert.LooksReallyCool(button); // not possible
Then there was the audio related code I worked on. Passing audio to the Windows MME APIs and checking that the sound played back correctly without stuttering. Or passing audio through a DSP algorithm to speed it up to double speed and automatically increase the volume in quiet bits.
Assert.DoesntStutter(audio); // not possible Assert.SoundsGood(audio); // not possible
I could go on. How does an automated test check that a webpage renders correctly on IE and FireFox (or harder still, on Mac and Linux)?
And here lies the trouble with the documentation for Unit Testing frameworks. It conveniently ignores these awkward cases and gives us an example of a BankAccount class that has a Withdraw method. Classes that add numbers or sort lists are ideal candidates for automated testing.
The observation that a large proportion of your classes cannot be meaningfully tested with an automated test therefore puts off many developers from writing unit tests at all.
But what if the main benefit of unit testing was not actually testing? Here's my grand (but unoriginal) idea: the main benefit of unit testing is not testing, but design principles.
Having a unit test for each and every class in your project means that each and every class is used at least more than once. It forces you to design classes that are loosely coupled. It will highlight hidden static dependencies. It will also enforce separation of GUI and business logic.
The trouble with thinking that Unit Testing is about testing is that "untestable" classes won't have unit tests written for them, which in turn means more badly designed classes. In short, if all your unit tests ever do is create instances of classes and call their methods without doing any checking of what they return at all, you will still benefit from greatly improved design.