The Three Rules of TDD.


Over the = years I have=20 come to describe Test Driven Development in terms of three simple rules. = They=20 are:
  1. You are not allowed to write any production code unless it is to = make a=20 failing unit test pass.=20
  2. You are not allowed to write any more of a unit test than is = sufficient to=20 fail; and compilation failures are failures.=20
  3. You are not allowed to write any more production code than is = sufficient=20 to pass the one failing unit test.

You must begin by = writing a=20 unit test for the functionality that you intend to write. But by rule 2, = you=20 can't write very much of that unit test. As soon as the unit test code = fails to=20 compile, or fails an assertion, you must stop and write production code. = But by=20 rule 3 you can only write the production code that makes the test = compile or=20 pass, and no more.

If you think about this you will realize that = you=20 simply cannot write very much code at all without compiling and = executing=20 something. Indeed, this is really the point. In everything we do, = whether=20 writing tests, writing production code, or refactoring, we keep the = system=20 executing at all times. The time between running tests is on the order = of=20 seconds, or minutes. Even 10 minutes is too long.

Too see this in = operation, take a look at The = Bowling=20 Game Kata.

Now most programmers, when they first hear about = this=20 technique, think: "This is stupid!" "It's going to slow me = down, it's=20 a waste of time and effort, It will keep me from thinking, it will keep = me from=20 designing, it will just break my flow." However, think about what = would=20 happen if you walked in a room full of people working this way. Pick any = random=20 person at any random time. A minute ago, all their code worked. =

Let me=20 repeat that: A minute ago all their code worked! And it doesn't = matter=20 who you pick, and it doesn't matter when you pick. A minute ago all = their=20 code worked!

If all your code works every minute, how often = will you=20 use a debugger? Answer, not very often. It's easier to simply hit ^Z a = bunch of=20 times to get the code back to a working state, and then try to write the = last=20 minutes worth again. And if you aren't debugging very much, how much = time will=20 you be saving? How much time do you spend debugging now? How much time = do you=20 spend fixing bugs once you've debugged them? What if you could decrease = that=20 time by a significant fraction?

But the benefit goes far beyond = that. If=20 you work this way, then every hour you are producing several tests. = Every day=20 dozens of tests. Every month hundreds of tests. Over the course of a = year you=20 will write thousands of tests. You can keep all these tests and run them = any=20 time you like! When would you run them? All the time! Any time you made = any kind=20 of change at all!

Why don't we clean up code that we know is = messy? We're=20 afraid we'll break it. But if we have the tests, we can be reasonably = sure that=20 the code is not broken, or that we'll detect the breakage immediately. = If we=20 have the tests we become fearless about making changes. If we see messy = code, or=20 an unclean structure, we can clean it without fear. Because of the = tests, the=20 code becomes malleable again. Because of the tests, software becomes = soft=20 again.

But the benefits go beyond that. If you want to know how = to call a=20 certain API, there is a test that does it. If you want to know how to = create a=20 certain object, there is a test that does it. Anything you want to know = about=20 the existing system, there is a test that demonstrates it. The tests are = like=20 little design documents, little coding examples, that describe how the = system=20 works and how to use it.

Have you ever integrated a third party = library=20 into your project? You got a big manual full of nice documentation. At = the end=20 there was a thin appendix of examples. Which of the two did you read? = The=20 examples of course! That's what the unit tests are! They are the most = useful=20 part of the documentation. They are the living examples of how to use = the code.=20 They are design documents that are hideously detailed, utterly = unambiguous, so=20 formal that they execute, and they cannot get out of sync with the = production=20 code.

But the benefits go beyond that. If you have ever tried to = add unit=20 tests to a system that was already working, you probably found that it = wasn't=20 much fun. You likely found that you either had to change portions of the = design=20 of the system, or cheat on the tests; because the system you were trying = to=20 write tests for was not designed to be testable. For example, you'd like = to test=20 some function 'f'. However, 'f' calls another function that deletes a = record=20 from the database. In your test, you don't want the record deleted, but = you=20 don't have any way to stop it. The system wasn't designed to be=20 tested.

When you follow the three rules of TDD, all your code = will be=20 testable by definition! And another word for "testable" is = "decoupled". In=20 order to test a module in isolation, you must decouple it. So TDD forces = you to=20 decouple modules. Indeed, if you follow the three rules, you will find = yourself=20 doing much more decoupling than you may be used to. This forces you to = create=20 better, less coupled, designs.

Given all these benfits, these = stupid=20 little rules of TDD might not actually be so stupid. They might actually = be=20 something fundemental, something profound. Indeed, I had been a = programmer for=20 nearly thirty years before I was introduced to TDD. I did not think = anyone could=20 teach me a low level programming practice that would make a difference. = Thirty=20 years is a lot of experience after all. But when I started to use = TDD, I=20 was dumbfounded at the effectiveness of the technique. I was also = hooked. I can=20 no longer concieve of typing in a big long batch of code hoping it = works. I can=20 no longer tolerate ripping a set of modules apart, hoping to reassemble = them and=20 get them all working by next Friday. Every decision I make while = programming is=20 driven by the basic need to be executing again a minute from now.