TDD – a better option for developers with ADD
September 8, 2012 3 Comments
So I’m back on the unit testing kick again. In my defense, I do spend an awful lot of time thinking about them – either feeling guilty about not writing them, or thinking about them as I code them, then (optimistically making it sound like I write tests first) or thinking about the testability of a particular piece of code as I write it. They are dear to my heart, in a strange kind of way. Part of me loathes them – being a pale approximations of actual applications behaviour, and the rest of me has only ever found them useful. Either proving my code, having decisions I (or those before me) have made confirmed to be conscious or driving my design. And that’s what I want to talk about. Test Driven Development.
I’m fairly convinced that most people don’t have a firm grip on what Test Driven Development is. Before we begin properly, I’d like to mention that I have actually read the book by Kent Beck. Including the code examples!
Now that my credentials are clearly proven and sufficient, I’d like to start by saying that test-first and test-driven development are not the same thing. That’s where most people get it wrong. Both are Good Things, but I favour test-driven development for a couple of significant reasons.
Test first development forces the pre-thinking favoured by people who grew up in the waterfall school of thought, who’ve graduated to more modern alternatives. It recommends some easy-to-test interfaces, forcing one to understand the interactions between modules (where, if you’re paying attention, is where a lot of bugs find places to take root) and it takes a lot of discipline to implement. The latter being the reason it’s not my preferred option. Most of the time I’m writing the test, my mind is wandering on to how I’m going to code up the implementation. This is often used as a selling point of test-first development, but I consider it a distraction. As I write more and more of the test I’m holding more and more of the code structure in my short term memory. So by the time I’ve written the test, I know how I want to code up the implementation.
Except that I’m a human. I don’t have extraordinary powers of memory to hold it all in there. I can only see the happy path, so if something doesn’t go according to plan during the coding phase, I need to deviate and the rest of my design (and potentially my test, but usually not) is often invalidated. Because I can see the design unfolding in my head, and because I don’t trust my memory farther than I can throw it, I will start coding up as soon as I have something resembling a test case. Maybe the happy path only. At best. So instead of trying to solve a single problem, I’m trying to solve about four, one of which being how to evolve a better memory capacity.
But how does Test Driven Development differ from that? Well, it’s essentially Test-First, but in smaller chunks. It admits that the vast majority of us do not have super-human memory powers (where was that X-man?) and gives us room to get things wrong. First, we should write the smallest possible failing test case, then code the smallest amount of code possible to make it pass (yes, if it asks for a random number, you really should roll a die). And keep building little pieces at a time like that until you have a full implementation. Then refactor. Always refactor. Writing code this way will keep it simple, but there’s often a cleaner design which will present itself to you along the way. You already have tests to cover what you’ve done at every single point, so you needn’t wait until the end to refactor – do it at the most opportune moment (which can’t be prescribed, only experience can teach you that, as it will likely be different for each individual piece of code).
And now, I think it’s confession time. Just this week, it was pointed out to me that one of the pieces of work I did was almost completely, but not entirely broken. And I had tests wrapped around it. This was a bug reported to me after test, so I thought I would write a failing test case (test-first) to capture the problem. I didn’t capture the case properly the first time (but had implemented it thusly) and when the mis-understanding got, rightfully, bounced back to me from test again, I modified the test slightly to cover the new case and then changed my implementation. Unfortunately, there was a time lag between these events and my thinking wasn’t modified as the test was. So something that was (nearly) tested the happy path wound up breaking some related functionality quite considerably. So the lesson, other than that I’m fallible and all my ramblings here should be taken with a grain of salt, is that no testing strategy will save you from yourself. If you’re thinking is broken, then so will the code be.
TDD is indeed a great thing.
Developers love TDD, because all other things being equal, an environment that does TDD is more mature than one that does not.
Testers love TDD, because it means less regression workloads on them.
Managers love TDD, because having fewer potential gross breakages in software releases means a more steady path to the top of the corporate ladder.
Capitalists love TDD, because with all the above people constantly happy, they can get away with not giving them pay rise.
Oh Xing, how will our team survive without your wonderfully unique point of view? I agree with some of what you said, but personally, I don’t see the connection of TDD and the economy. However, it’s entirely possible that that’s just my slightly narrower point of view coming in to play!
Pingback: Big Up Front Design Considered Scary Beyond All Belief | A Million Code Monkeys