{ by david linsin }

June 02, 2008

Nothing wrong with TDD, right?

Have you ever heard of Test Driven Development? Wikipedia says the it is

... a software development technique consisting of short iterations where new test cases covering the desired improvement or new functionality are written first, then the production code necessary to pass the tests is implemented, and finally the software is refactored to accommodate changes. The availability of tests before actual development ensures rapid feedback after any change. Practitioners emphasize that test-driven development is a method of designing software, not merely a method of testing.

To me it's simply writing your unit tests, before implementing your classes. It's supposed to help you think about the design and in particular the interface of your classes upfront. I like this notion a lot! Thinking before coding - that's a good thing, right? Before I start my ramblings, let's here what Cédric Beust, co-founder of the TestNG framework has to say about TDD:

... When you do TDD you tend to promote micro-design over macro-design. Micro-design means that you are focusing heavily at a very fine level which is the method....but what I found is that after a few years programming you start getting a good feeling with intuition, right away when you're solving a problem of what these extra classes are going to be, what the architecture, the design, the inheritance, how they interact with each other. But TDD is preventing you from going too far ahead...it leads you to create things that are so small that you end up throwing them away because they are really a first iteration and then you modify them and then you rewrite your test. So to me that's micro-design. Macro-design is more when you start thinking: "I'm going to need this interface, I might as well write it now, I'm going to need this extra class, I'm going to write it now; it's going to be empty right now". And those are things that are frowned upon by test-driven development practices and that bothered me a little bit.

The statement is taken from an interview at InfoQ, if you are interested in the verbatim version of this quote, jump to question 6.

So basically Cédric spilled out exactly what my problem is with TDD! I've tried it - really, but writing my tests before implementing my classes just doesn't work for me. Let me tell you why.

I see coding as an iterative and incremental process, Cédric calls it macro-design. You have this basic idea of what your class is supposed to do. In the first step - or iteration therefore, you give it a name, which is supposed to denote the concept of your class. Maybe you also add some javadocs to explain your brainchild. At this point you probably don't have any methods yet. You don't really know in detail how your class is going to work, thus you can't generate your test cases. Most likely you don't want any test cases at all and that's exactly where the experts disagree.

Most of the time, in my experience, a unit test of a class reflects its behavior. You can implement a black box test, but as soon as your class doesn't stand alone, you have to mock the behavior of your collaborators. I'd argue that in 90% of all cases your class collaborates with some other classes or components. So if you start coding your test cases upfront, your not only defining the interface, but also the behavior of how your implementation collaborates with dependencies. If you are working on a component with several different classes and you are starting with implementing test cases upfront, you find yourself constantly refactoring those tests as your implementation evolves. I believe that's not what you want to spend most of your time on.

It's almost a paradox: you need test cases and it's sounds reasonable to write them before your implementation, because then they actually exist and you cannot forget about them. At the same time it doesn't make sense to test something, which doesn't exist yet or you only have a rough idea of how it's actually going to look like.

I like unit tests and I try to write one for each class I put out there. Some people argue, that writing your unit tests upfront increases the quality of your code. I think it's not the unit tests, it's merely giving some thought about your design, before starting to code. However, when I'm in the middle of an "implementation spree", refactoring test cases is one of those annoying and tedious little things that you constantly have to deal with as a developer. And those little things kill the mood - big time.

6 comments:

Jonathan said...

I think the problem is TDD+Unit Tests.

If your tests are broader in scope you might have more luck. For example, the test may be "read file format X", "generate report Y", or "allow the user to perform action Z". These kinds of tests are not necessarily automated, but they are far less susceptible to code churn than low level tests.

Prashant Jalasutram said...

david,

I think at this moment i need to agree with you.

Thanks
Prashant Jalasutram
http://prashantjalasutram.blogspot.com/

Anonymous said...

TDD- what is this supposed to deliver again? Oh yeah, that's right, code quality.... "This code has been tested...."

The problem is of course that even the most simple program has so many possible states that a computer the size of the universe composed of gates the size of atoms which in turn switch states at the speed of light couldn't compute all the possible states of that program so that you can say you've tested it.

So what does TDD do? Why it tests the subset of data that the programmer determines, through experience and intuition, are likely to to cause trouble - corner cases, pathological input etc.

And this is different from what programmers have always done ... how again? In know I should know the answer to this b/c TDD priests have been preaching for years now, but I just can't remember it.

The fact is, TDD promises something undeliverable- throughly tested code. What it relies on is exactly what it denies the sufficiency of - a programmer's analytic understanding of the code and ability to understand, without testing, what a program will do. That understanding is just how the data that is unit tested is selected from the universe of data which could be selected.

But as I said, this is just what programmers have always done.

Like it or not, the best and ONLY reason programs work as expected is because there's an experienced developer sitting there who understands how it works.

I know there's a level of management that hates to hear that, because it immediately implies a dependency upon individual developers. TDD found its most sympathetic hearing in the corner offices because it promises to increase the interchangeability of developers. A best interpretation is TDD attempts to capture best practices of good developers, and a more realistic interpretation is it churns out a deaf mockery of those practices and imposes a leaden, mechanical and pointless exercise of busy work and wasted time.

TDD is a kind of false assurance or hand-holding for people who are afraid of their code base. At some point, corporations will learn that there IS a talent market worth paying a lot of money to participate in, but its not at the CEO level- it's at the level of the individual developer. Writing code is not flipping burgers and the interchangeable "labor" model that applies to McDonald's isn't going to fly in IT.

david said...

anonymous > The fact is, TDD promises something undeliverable- throughly tested code. What it relies on is exactly what it denies the sufficiency of - a programmer's analytic understanding of the code and ability to understand, without testing, what a program will do. That understanding is just how the data that is unit tested is selected from the universe of data which could be selected.
...
TDD is a kind of false assurance or hand-holding for people who are afraid of their code base.


Awesome rant man! I wish I could come up with something like that! Thanks for your thoughts!

anonymous > Writing code is not flipping burgers and the interchangeable "labor" model that applies to McDonald's isn't going to fly in IT.

Amen!


jonathan > If your tests are broader in scope you might have more luck.


Actually, I always thought unit tests are the granularity that applies to TDD. I mean thinking about a whole use case and writing tests for that upfront would almost be like programming with your eyes closed. You might not have any idea how your environment works, since you have no running code yet.

I concur with your coarse grained test idea, in case you want extend an existing component. Then you would have something like a Characterization Test, to ensure functionality after you adopted your code.

Bob Saggett said...

I am trying out TDD at the moment for my own, home-grown projects. I certainly like elements of it but I also like a little up-front design because I hate to throw things away.

However, the strength of evangelism around this and other agile methods is frightening. I personally know a TDD proponent who doesn't realise that his attempts to make the development process less bureaucratic actually gives a ridiculously rigid development methodology. "You cannot write any code without a test" and "A test should cover the smallest unit of code possible" mean that creativity is limited to that of the MacDonalds employee, slapping another burger into the machine.

Good developers are creative types that should not be constrained by these methodologies, no matter how new or old they might be.

asifsehzaad said...

ok... I agree with the points being pushed here about monkeys pressing buttons... but the real deal with TDD is to check the functionality of your code/application.... and i couldn't stop writing tests...just because some cool ass comment comparing macdonald burger plunger with programmers.... tests are important (good programmer bad programmer it doesn't mean shit...)and you need to cover your application/code in your tests.. that is how i take TDD to be (BDD also included)...


com_channels

  • mail(dlinsin@gmail.com)
  • jabber(dlinsin@gmail.com)
  • skype(dlinsin)

recent_postings

loading...