The Skeptical Methodologist

Software, Rants and Management

Solving the “Problems” with TDD

First, read Dalke Scientific’s criticisms of TDD, or Test-Driven-Development.

The important thing to note is that when we talk about TDD, we’re talking about writing a test to drive the design of an API, you ‘test first’ before the implementation is written.  This is often juxtaposed to testing after an implementation is known.

This is an important thing to note, as I think taking DS’s criticism’s in this light makes sense of many of the problems the author finds with TDD approaches.  Namely, as is often said, TDD should not supplant many other testing strategies for code.  DS points out, rightly so, that some authors like Uncle Bob and Kent Beck have oversold TDD as yet another silver bullet.  Not all steps to the development process naturally flow from TDD.  In fact, as some have pointed out, TDD has little rigorous advice towards incorporating domain knowledge, or exploring a domain in which you have very little experience.  You can’t TDD your way through a Sudoku solver, or a ray tracer.  Instead, you need to understand the algorithms behind those solutions.

TDD is specifically about driving clean, reusable interfaces from well understood requirements.  Hah!  Since when are requirements ever well understood?  But that’s ok, since TDD is frequently packaged with a good dose of refactoring, we should expect any accomplished TDD’er to also respond well to changing requirements.  So, let’s rephrase that – TDD is about driving clean, reusable interfaces from requirements.  That’s all – it is no guarantee of code quality.  Furthermore, as DS points out, it is no guarantee of code coverage either!  Simplistic TDD would state that no line would be constructed without a test requiring its behavior.  That is true.  But recall, as I just stated, TDD is packaged with refactoring, which itself (as DS rightly notes) makes no guarantee on test coverage.  On an opposite note, one place where I disagree with DS is where he makes a big deal out of the performance requirements of finding Prime numbers.  TDD solutions are (or should be) entirely derived from requirements since they more or less are code implementation test forms of the requirements.  If Uncle Bob’s self-imposed requirements on his prime finder don’t specify processing speed, then his approach is legitimate.  What DS should have argued is that in the case of algorithmic code like a prime finder, generally speaking performance matters and would have been specified formally in one way or another (which implies that performance should be tested under a TDD scheme and would have ruled Uncle Bob’s solution out.)  I do sympathize with DS’s aesthetic revulsion to Uncle Bob reinventing an already understood algorithm in a slower form to prove that TDD works, but that’s a side issue.

So, I’d like to reset the debate around this claim:  TDD is a good “enterprise” technique to drive clean, reusable interfaces with high, but not perfect, code coverage.  I use the dreaded term “enterprise” here to mean “corporate” style requirements that are somewhat well understood versus blue-sky or academic research requirements, and they also tend to be brute force heavy and algorithmically light.  Oddly enough the ‘plumbing’ code this description provides makes up a huge percentage of what we, as software developers, are expected to write.  So TDD certainly has its place.

Where is that place?  Let’s go a little into testing theory real quick and it will jump out at us.  Traditionally, there are ‘verification’ tests and ‘validation’ tests.  Verification asks “did we build the thing right?”, while validation asks “did we build the right thing?”.  Verification asks questions about quality, about coverage, about safety and reliability.  It makes sure that, whatever we built, we did it ‘well’, that it has ‘quality’ or ‘craftsmanship’.  Validation, on the other hand, asks whether the thing we built was the thing the customer asked for in the first place.

There’s another dimension to testing, commonly referred to as white-box vs black-box.  Black box tests treat the implementation as an unknown and strictly tests the interface.  White box techniques look at the implementation specifically and try to break it.  They are both beneficial as looking at software as a black box generally allows one to test it a lot more aggressively since they have no ‘clues’ via the implementation to try to break it, so they just throw everything they have at boundaries and faults.  White box is beneficial for exactly the opposite reason, the implementation gives the tester hints on what might break via reverse reasoning (assume it broke and then work backwards to see what inputs would break it.)  Black box testing is also easier to automate ‘tests’ for, like fuzz or smoke tests, while white box styles allow better ‘static analysis’ techniques.

You ‘need’ to cover all 4 quadrants of testing for a good strategy.  DS points out the ‘flaws’ in TDD, but in the context of testing theory, these are not flaws.  The characteristics of TDD put it squarely in the “black box validation” camp, along with techniques like requirements traceability and use case analysis.  I’d say in that company, it does quite well.   The “traditional” unit tests DS refers to fall more in the “white box verification” camp, i.e., you know the implementation and you’re specifically checking it for quality.  This quadrant needs to be supported to, and there are a myriad of techniques to do so (some of my favorite include Joel’s ‘completely separate testing team’ approach as well as Design-By-Contract.)

So, to sum up, the “problems” with TDD aren’t problems at all: they’re endemic to the quadrant of testing TDD is associated with.  TDD has been oversold as a magic bullet by its promoters, that is for certain, but it is not synonymous with “good testing” as the original author states.  It has a very specific place in a “good testing” strategy, namely, taking requirements and directly stating them as automated validation tests of any potential solution’s interface.

December 29, 2009 - Posted by | Uncategorized

1 Comment »

  1. Hi SM (since I’m DS after all ;)!

    You wrote “If Uncle Bob’s self-imposed requirements on his prime finder don’t specify processing speed, then his approach is legitimate.”

    I disagree with that. TDD is built on unit tests which must be fast enough to run often. Taking 2 minutes to run a single test is not acceptable from a TDD viewpoint, even if the specification doesn’t prohibit it.

    I did not say or mean at all to imply that TDD has ‘flaws’, only that it has limitations, and if used as the only development methodology, will lead to problems. I happen to believe that the other methodologies are more capable than TDD, but that does not mean I think it’s a flawed system. If that tone came across in what I wrote, I do apologize.

    TDD is not in the black box validation camp, and I don’t see how it can be. The test cases are clearly based on known limitations of the software, as written by the developer. They are not found using the methods of black box testing, which would have include better input range coverage than the self-selected tests used to drive the algorithm development.

    In my essay I pointed out that the TDD examples for these cases were actually not good at taking the requirements and converting them into automated validation tests. Asking for fib(43) should be a clearly acceptable request given the requirements specification. It’s easily computed and the result fits into a signed 32 bit integer. But the chosen algorithm won’t end for that until about 2**43 function calls have been done. At 1 ns per call that would 2.5 hours. A better algorithm needs about 43 additions.

    TDD promotors say that TDD is not a testing strategy at all. That’s why it does not have the goal of coming up with validation tests like you mentioned, only tests that drive the software development.

    Best regards — Andrew Dalke

    Comment by Andrew Dalke | December 30, 2009 | Reply


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: