TDD – it’s all about the concrete.

A recent thread on the LRUG mailing list got me thinking again about Test-Driven Development. Actually, more accurately, it made me furious and a bit depressed – macho posturing, regurgitated nonsense, deliberate misunderstandings. All creating the impression that there’s a debate about whether TDD works, and that that debate should be about individual developer competency. Real developers don’t do TDD, or something.

Most of the discussion wasn’t actually about TDD. So, perhaps we’d best start by defining what we mean here – or at least, the way I think you should see TDD. Then perhaps, we can talk about its merits or otherwise in a rational way. And, along the way let’s try not to dig new foxholes that piss-poor developers can snipe from.

TDD is a practice, situated in a discipline called Extreme Programming (XP). TDD is the second worst-named term in programming, only worsted by Extreme Programming itself. Seeing the practice within the broader discipline is important though, as we’ll come to see.

The reason it’s poorly named is that there’s really a two-step process that should be at work when practising TDD – test and code together and relentlessly refactor.

This is the first place where XP asserts itself. XP requires programming in the concrete, that our goal should be working software – indeed, that software only has value when it’s working. You can take this on many levels, but one I find enlightening is the pith of ‘code is abstract, working software concrete’. If you program all day (or in my past experience, for months at a time) without ever running your code, without making it concrete, then you have not been in the business of producing working software. You are making software that might, not software that does.

Most people agree at some level with this, although some still suggest that code readthroughs are the way to assert working software; some even suggest that thorough documentation is the answer. They may suggest this, even strongly defend it, but in practice those same people usually try to run the code before releasing it. Of course, XP does assert that code is best written in dialogue with other programmers, hence the practice of pair programming. This is, at most, a distraction from arguments about TDD.

As practitioners of TDD, we run our code all the time. We build harnesses to exercise our code all of the time – we call these harnesses tests. And we keep them around when we’re done with them. I think we’d say that we call them tests because they don’t just run the code, they check it too. But the reality is we call them tests because they’ve always been called tests and it was easier to sneak the practice into the typical IT shop.

(Another blog post waiting to be written here about BDD – why are we moving these harnesses away from code and towards documentation? It’s going the wrong way.)

I always find that this far, it’s easy to take most developers along with me. Nothing too controversial to this point, usually the naysayers here are people defending their own poor practices, and they know it. From this point in the real disagreements start…

The first thing the nodders add to this process is what I’ve snarkily called ‘the golden age before code’. This is some bizarre time before we start writing code where design is done and things are clarified. This is, according to some programmers whom I wouldn’t employ, where the real work is done. Factories are built and mouldings designed. Whoops, wrong industry. I mean, blueprints are drawn up and steelwork ordered. Whoops, wrong industry again.

What exactly is being designed in the golden age? The best answer seems to be code, not working software. In the golden age we are two steps removed from working software. We, as TDD zealots, reject this way of working – choosing to make code the level at which we design, not documents and diagrams. We don’t test our designs by coding them, we test our code by running it. This is an important point. TDD says we don’t stray far from the concrete world of running, working software – and crucially that we start in that world.

No API written without a client using it; no class interface without a instantiator; no method without a caller. Again, we call these second clients tests, and they’re part of the not-so-secret TDD magic that leads to better code. We try to keep them simple and precise, and we let the act of making the code run instruct us on how it should be written.

We find out early when interfaces are awkward, when classes have too many responsibilities, when methods aren’t needed and interfaces are bloated. We find this out by writing code, not by reading paper or drawing lines on whiteboards. And, us TDD folks say that this is the best way to do so. Because concrete working software is what we’re building. And because no plan survives the reality of battle. We know we don’t have all the answers when stood at the whiteboard drawing pictures; we know that our experience is pretty narrow when it comes down to it; and if we knew how to do it already, someone would have already done it. This humility is why TDD practitioners get a bad name for being arrogant. No, I don’t understand that either.

When coaching on this point in the past, I’ve used some pithy comment about how whiteboard ink was cheap. Then I found out how much those pens actually cost…

We’re nearly done defining TDD – and I haven’t even mentioned correctness or validation yet. Perhaps I won’t. It’s not that important in my understanding of TDD. Working software. That’s a better way to put it.

The final bit of TDD, refactoring, is really important and the one that gets mentioned the least. Again it has to do with the limits of our pre-cognition and a relentless prioritisation of the concrete over the abstract.

Refactoring is the process by which the code becomes more abstracted and better for it. That is, with TDD, we’ve seen that we don’t start with an abstract idea of what the software should be, untested and in a parallel medium. We start with working code. Refactoring is how we pull, pry and tease abstractions out of the code to make it cleaner, more pliable and more self-explanatory.

While we pull and pry these latent ideas and perspectives out of the code, we keep turning the code into working software by running our tests. We assert that the code duplication is the smell of these latent abstractions and we often start by trying to remove it. At this point, too, we care about naming and how the code expresses its intentions.

To my mind, this is where the smart folk shine. When people say that testing is hard, my response is often ‘yeah. But refactoring is the bitch.’ That’s one of the reasons you’ll find more written on testing than on refactoring, many more testing frameworks than refactoring tools. I don’t know why it gets so ignored in TDD discussions though, maybe we should change the name.

So that’s all really. TDD means something more than testing, and it pisses me off that we bog ourselves down in the kind of discussions that appeared on LRUG. If you want to have a pop at it, learn what it is first.

Now, we TDD-nuts believe that this ends up giving a better, less leaky, sharper design than we could have come up with on paper or at a whiteboard. You may not be able to accept that. But, as Tarantino might have said if he were a developer: that’s pride fucking with you. Fuck pride. Pride only hurts, it never helps.

In challenging TDD, I think you have to start by asking whether a process that goes from the concrete business of writing working software to an abstracted design is better than a process that goes the other way. I think it is, in fact I’m sure of it. An ex-colleague used to say ‘code doesn’t mind if it looks simple, but architects want to look clever.’ I think he had a point.

You can argue a hybrid model with some design (of boxes, on whiteboards) up-front has merits. I haven’t found, in my career, the results of that compromise to be useful. And I’m not sure the downsides of this approach, which tends towards over-engineering, are worth risking.

Anyway, the point of this post, which is longer and more abstract than I initially wanted was to describe what TDD is, why it’s a good thing, and to show that most of the criticisms I hear aren’t really talking about TDD at all.

Mocha-loka-choka

James Mead has released his Mocha stubbing and mocking framework for Ruby up on rubyforge.

From the README:

Mocha is a library for mocking and stubbing within tests using a syntax like that of JMock and SchMock.

Mocha comes in three parts:

1. Mocha – traditional mock objects with expectations and verification
2. Stubba – allows mocking and stubbing of methods on real (non-mock) classes
3. AutoMocha – magically provides mocks in the place of undefined classes

Stubba and AutoMocha are the main difference between this mocking library and others like FlexMock and RSpec.

It’s a smart bit of coding, and has good examples of meta-programming and fluent interfaces.

You can do cool things like this (but this just scratches the surface):


class StubbingLibrary
   def initialize(developers)
      @developers = developers
   end

   def release!
      @developers.each {|dev| dev.heap("praise","thanks")}
   end
end

class StubbingLibraryTest

   def test_should_heap_james_with_praise
      james = Developer.new
      james.expects(:heap).with("praise","thanks").at_least_once
      mocha = StubbingLibrary.new([james])
      mocha.release!
      james.verify
   end

end

I recommend you get it and read the README for yourselves – it has Star Trek examples, if you weren’t already convinced…