Kurser i Domain-Driven Design - Våren 2012




Saturday, January 29, 2005

A TDD Confession

A year or so ago I attended this, kind of dorky, presentation (or perhaps it was a workshop) on test driven development, TDD. Two guys were showing how to construct a class method by method by first writing tests for a method that did not exist yet and then add the actual implementation. It went something like this:

- OK, the requirement states that it should be possible to configure the component by supplying a size value when the component is created. Then there should also be a getSize() method that returns the current size. So we start off by writing a test for the getSize() method:
  Thing thing = new Thing(12);

assertEquals(12, thing.getSize());
The test was run, it failed and the getSize() method was adjusted to make the test pass:
  public int getSize() {

return 12;
}
The test was extended:
  Thing thing = new Thing(22);

assertEquals(22, thing.getSize());
The test was rerun. Guess what!? The test no longer passed! The code was now adjusted to make both tests pass. And this is pretty much how the presentation/workshop progressed; slowly more and more functionality was added by first writing stupid tests and then adding overly naïve implementations that would most surely have to be rewritten when the tests were extended. If I’m not mistaken, all this was in C#, to further incite my ignorance.

It was quite obvious to me that this was a waste of time; something I naturally found obligated to pass on to the guys giving the presentation. I also managed to come up with some other arguments against TDD:

  • Ad hoc coding from a test point of view will result in bad API design.
  • Slower development
  • Tested, but messy code due to much focus on tests, and less focus on the actual implementation.
Instead of going for the obvious argument bait, one of the presenters challenged me: "I suggest you try it, really give it a try and see if it works for you".

FF a year. Now I’m sitting here writing a lot of my code TDD style (I’d love to say "all of my code in TDD stylee", but that simply would not be true, also I don’t feel I have enough street cred for the additional e).

So what the hell happened?

It actually was quite simple, my motto being “now and then it happens that I from time to time try everything once”, I did just that. And TDD still sucked. So I tried again, and again, and things started to turn. My fears were not realized, quite the opposite indeed.

The API design didn’t deteriorate, but rather improved. It is really hard to write tests if your API is bad, if your methods aren’t crisp and your classes have a lot of unnecessary coupling to other classes. Naturally, I knew all this before… but TDD made it somewhat clearer.

The development didn’t slow down. I already used to write unit tests before I tried TDD, but not until after the implementation was done, and doing it the other way around didn’t slow down development. In fact, modern IDEs have sophisticated functionality to help you very quickly create the tested class from the test class. Also, the code quality has improved; if you don’t write the implementation until after the test is written there simply will be no untested code (Well, at least in a perfect world scenario, as long as you implement nothing that is not fixing a broken test, but you get the idea). You can now prove that your code works, and will continue to work after refactorings and other code modifications have been performed.

Yes, it is true. When you work with TDD the code is tested, and sometimes it is also messy. But this is a great opportunity to exercise your refactoring skills, work the code, make it shine! This can be done in a safe manner now when you have your tests. Sweet! If you take your time to do this, the code has the potential to look better than ever.

Find a good balance between writing code and tests. You don’t want to write too large chunks at a time so that you code functionality that has no corresponding tests, but on the other hand you might want to stay away from the over simplistic approach described at the beginning of this text. You will have to find your own pace and scope, a test coverage tool can come in handy here, it will tell you if you slip and start to write code that have no corresponding tests.

TDD works especially well if you work with an architecture based on a plain object domain model, adding new functionality to your domain classes will be a piece of cake from now on. Also, a good IDE with excellent refactoring support, such as IntelliJ IDEA or Eclipse, is more or less necessary. Actions such as "implement method" and "introduce variable" make the TDD developer’s life so much easier. If your IDE is lacking in this department, you’re missing out on much of the fun (go ask your manager for an upgrade now)!

The workshop presenters were unfortunate enough to have me in the audience, but to repair some of the damage I’ll pass forward the TDD challenge: Try it, you might even like it.

Peace, out, till next time,
/Patrik

For coverage tools, Try EMMA, or Clover.