On my continuing journey through this thrilling world of professional software development, I frequently encounter teams who hold no truck with this crazy new-fangled concept of unit testing. Heck, truth be told, I sometimes come across teams who don't use source control, but that's by the bye.
In these situations, I try my best to lead by example, continuing to practise TDD on any new code that I cut, and insisting on covering with unit tests any pre-existing code which I have to modify. Sooner or later, this prompts someone to ask why I am doing this (often insinuating that I am wasting my time, and by implication the client's money). At that point, flustered, I invariably struggle to remember or adequately articulate the many and various ways in which unit testing makes me a better developer. So, consider this blog post a personal aide-mémoire to help me the next time this occurs (as well as being a homage to Jake Shillingford's second-best song).
1. I know that my code works.
Let's get the obvious one out of the way first. If I take the time to write unit tests for my code, then I know that the code "works" (i.e. that it behaves as I intended, or hopefully as the specification requested). Any time I want to check that my code works, I can do so with a few clicks. The confidence and feeling of well-being that this brings can't be underestimated. If you don't have unit tests, how do you know that your code works?
2. I know when I'm finished.
As you'll know if you've read my post professing my undying love for Remember The Milk, I'm fond of organising my life through to-do lists. When I'm developing software, the unit tests that I create before developing the code act as my to-do list, help me focus on what it is that I'm trying to achieve, without getting distracted making unrelated changes to the codebase. When all the tests go green, I know I'm done and can move on to the next task.
3. I can develop cleaner APIs.
I'm not much of a UI developer; I spend much more of my time working in the middle tier, making class libraries, object models, WCF services and the like. When practicing TDD, I write tests to consume my code before I write the code itself. This forces me to think about things like method signatures and member visibility. Should that method really be public? Do I really need that "out" parameter? Will all those overloads get used? If I didn't write tests for the middle tier in this way, I wouldn't consider these questions in quite so much detail, and these issues wouldn't come to light until I (or worse, someone else), began consuming my code for real.
4. Testable code is well-designed code.
This one came as an unexpected surprise to me after I had been writing unit tests for a while and forced myself to make the effort to test code which I initially felt was difficult to test.
It turns out that many of the techniques which can be used to make code more testable are the exact same techniques which make code better designed, understandable, flexible and maintainable.
Be honest, when you're cutting code, how often do you consider things like Low Coupling, High Cohesion, and adhering to the Single Responsibility Principle? Probably not often, I'll bet. I know I didn't. But since I started asking myself "How can I make this code testable?" I've been delighted to realise that my code magically acquired these properties as a pleasant side effect. Similarly, using techniques like Dependency Injection and Inversion of Control along with Mocking Frameworks for the purposes of testability also serves to make the solution more open to change, and reduces dependencies during the development process (no longer do you hear "Gee boss, I'd love to get started writing the Foo class, but it depends on the Bar class, and Dave hasn't finished writing that, what with the accident with the bus and all…").
5. I can refactor mercilessly and fearlessly.
"Nel-son!!"
I like refactoring. No, wait, scrub that – I love refactoring. Some people get their kicks from gardening, well, I could spend many happy hours weeding a big tangled codebase. Now, in the past, this has gotten me into trouble, when the "improvements" I've rashly made have inadvertently caused bugs (sorry John, Sion, Scott and Colin!). Since adopting TDD however, I've been confident that my code is sufficiently covered by unit tests that I may refactor mercilessly, safe in the knowledge that the test suite will highlight any unintended side effects. This results in a winning combination of clean code and co-workers who don't have an urge to throttle me.
6. I Release Fewer Stupid Mistakes.
"Ah, yeah, silly me, I just copied-and-pasted a line in the DAL, and now all your customers are displaying as having the same forename and surname! Smith Smith, Brown Brown, ha ha!"
Perversely, some coders seem to wear their really dumb mistakes as a "badge of honour", proof of their fallibility, a sign that they are human. It's as if they are suggesting to their boss/client not to worry, that they only make dumb mistakes on the easy code, but concentrate much harder when coding the tough algorithms. Honest, guv'.
I don't buy this crap. Stupid mistakes are stupid mistakes, period. I think that dumb mistakes, uncaught at an early stage, waste the time of the test team, result in rework, and ultimately waste the client's money. That's nothing to laugh about. Covering even the simple, easy code with unit tests reduces the likelihood of these knucklehead bugs getting through, increasing my likelihood of a "right-first-time" reputation. And that's something to be proud of.
7. Bugs don't come back.
"But I'm sure we fixed that bug months ago!"
Before I fix a bug, I write a failing test. That test then becomes a permanent fixture in the test suite, and therefore I can be confident that no "undead bug" will ever come back to haunt me. It's a very simple concept, but it works. Bug reactivations imply loose development practices, and that's not an implication I want to be made about my work. Kill the bug once, and keep it dead.
8. It's the fastest way to test my changes.
Testing my changes through artificial unit tests is invariably quicker than testing the functionality by calling it through "real life" use of the overall system.
Suppose that I'm working on amended postage charge logic for an online retailer - I could test this by either:
a) Repeatedly manually walking through the application, adding a product to my basket, attempting to "check out", and calculating whether the displayed postage charge is as I expect, or
b) Writing a unit test once to automate the testing of the new logic.
Gee, let me think about that for a while...
9. I spend more time coding, less time in the debugger.
Stop me if this conversation sounds at all familiar:
"How was your day today, darling?"
"Ugh, don't ask. I spent, like, seven hours trying to track down a bug. I pulled out Reflector, and even started debugging into the .NET framework class library - I was convinced for a while there it was a problem in System.String. But it turned out I just had some Boolean logic the wrong way round."
"That's nice, dear. More mange tout?"
Visual Studio has excellent debugging tools, but still, I'd rather be spending my time cutting new code, thanks all the same. I'm sure the clients who pay my invoices feel much the same. Catching the bugs early, through effective unit testing, means that I spend far less time in the debugger.
10. Writing documentation and sample code is boring.
Visiting the dentist. Cleaning the bathroom. Writing documentation. All things which I'd rather put off until tomorrow, thank you very much.
If, on any given day, I have the choice between firing up Word and documenting my class library, or firing up Visual Studio and adding shiny new functionality to the same library, well, it's no contest.
But here's the neat thing – having taken the time to create a comprehensive suite of unit tests for my own, selfish purposes, it turns out that potential consumers of my library can benefit from them as a de facto set of usage examples and documentation. Yay!
11. I can hand the code over to other people.
"Dave's the only person who understands that code."
Oh really? What happens when Dave is on holiday? Or quits to get a better-paid job? Or gets hit by a bus? That's a bit of a risk for your project, surely.
Maybe you're a Dave, and your unique knowledge of a particular system means that you only ever get to work on that one application. Shiny new projects come along, and get handed to your colleagues, because you're too busy supporting the Legacy App From Hell, until you quit to get that better paid job, or walk in front of a bus in desperation.
If, on the other hand, you've covered the code in unit tests, then you have effectively documented the codebase, provided example usages, and made it near-impossible for any future developers to break existing functionality due to their ignorance of the system. They can try making a change, run the unit tests, and instantly find out whether their change works. This helps to make possible Collective Code Ownership, a variety of work for all members of the team, and no risky dependencies on Dave and his jaywalking habits.
12. I'm A Professional
I'll return to what I said in the opening sentence of this article – this is professional software development. This isn't just my hobby, or my day job. It's my career, my business, my passion and the livelihood of myself and my family. I strive to improve and hopefully excel at it. All the research and my own experience suggests that practicing TDD results in fewer defects, saves time in the medium and long term, and is generally the approach to be expected of a professional. If you're looking for a quick-fix from a cowboy contractor, go elsewhere and save yourself some money… in the short term :-)
(All times GMT)
0907 - Later editions of some of the papers:
0900 - Congratulations to our American friends on your new President-elect. It's been a thrilling election to watch over the past year or so. Your democratic processes and inclusiveness truly are a shining example to the rest of the world. Well done!
0859 - Oops, I think I napped for a little longer than intended.
0140 - Tired. I just can't hack it like I used to. Think I'll stay up until 0200 and then take a nap for a while.
0109 - Wow, the projected results are sure coming through thick and fast this year. BBC reporting DEM 103 - GOP 34.
0100 - Pennsylvania's 21 electoral college votes stay with the Democrats.
0019 - Fox have a cool interactive map thingy - http://elections.foxnews.com/states_map/index.html
0015 - Ricky Gervais is now being interviewed on the BBC Election coverage - mmmm-hmmm...
Wed 05 Nov 2008, 0004 - and we have the first two states called. McCain 8, Obama 3.
2356 - The early editions of the UK newspapers seem pretty unanimous:
2349 - How comforting to have a Dimbleby to guide us through this craziness. "We'll slowly piece this thing together..."
2346 - I do so love the word "gubernatorial"...
2340 - Drudge - "Senate: Dems see 58 seats; Exit Polls Show Obama Big".
2333 - BBC and Sky presenters both have touch-screen displays to play with tonight. Both of them keep saying "Huh, let me try that again!"
2330 - Another summary site - http://howisobamadoing.com/
2300 - McCain's chance of becoming President is now trading at 6.3% on http://intrade.com.
2257 - Confused by all the polls and poll-of-polls? Try http://isobamapresidentyet.com/ - cuts to the chase!
2213 - Zogby says 311 - 174 with 53 unsure - http://www.zogby.com/50state/
2211 - Huh, polls close at 1900 local time? You seriously expect people to go out and vote before the local equivalent of Eastenders has finished?
2209 - Election updates from Twitter - let it wash over you - http://election.twitter.com/
2157 - The Times also have a constantly-refreshing page of coverage, rumours, and general goss - http://timesonline.typepad.com/comment/2008/11/rolling-guide-t.html
1418 – Here’s a good page to open in one of your election-night tabs – the BBC have a page which constantly refreshes with the latest news. http://news.bbc.co.uk/1/hi/world/americas/7700298.stm
1349 – As ever, I find myself amazed at the queues at polling stations and length of time that Americans are willing to wait to vote. Folks I follow on Twitter seem to think 15 to 45 minutes is the norm. In the UK we’re usually in and out in under a minute – but then we only get to place a single cross in a box.
1346 – Heh! Radio 4 are using W.G Snuffy Walden’s theme to The West Wing in their trailers for tonight’s election coverage.
1100 – Polls have opened in most of the Eastern states.
1058 – Obama wins by 15 votes to 6 in Dixville Notch, New Hampshire. Turnout was 100%. This is the first time the town has gone Democrat since 1968.
0940 - The live electoral vote map on intrade.com (which is based on real financial trades made by individuals on the probability of an event occurring) has been showing a 364-174 EV split for most of the past month, save for Missouri changing hands occasionally:
Up at 0511 with The Boy today.
As I’m watching the news and eating breakfast in the living room, the young scamp keeps turning off the TV, forcing me to get up and turn it back on again.
After four iterations of this game, I’m on my feet turning the telly on, as Benny runs round behind me, and pulls my mug of (fortunately lukewarm) coffee onto the floor. Cue much cursing from yours truly.
I run off to the kitchen to get some tea towels to mop up the mess, and return to find that he’s now decided to pour my breakfast cereal into the mix. Aaargh!
Follow that up with the usual packed train ride, the idiots at Leeds station who don’t understand the concept of “keeping right” on elevators, the inability of some commuters to fathom the new ticket gates, the long uphill walk to my current consultancy gig, and my own inability to fathom the bizarre “transponder” door-opening system to get into the building, and it has generally been an exhausting morning. And it’s not even 0900 yet.
I need coffee. Which is how this all started..