Tuesday, April 25, 2023

The Three Laws of Test-Driven Development are Useful but Incomplete

While out on a walk in early 2021, I was thinking about the best way of teaching Test-Driven Development (TDD) to other developers when I realized something unsettling: Robert C. Martin’s “Three Laws of TDD” are incomplete. I had read about the three laws in a blog post of his many years earlier and found the idea intriguing enough that I tried using them for a while. The results were good enough that I made them part of my TDD practice, and I shared them with others when teaching a TDD workshop or giving a presentation on it. 
 
Once I realized the Three Laws were incomplete, I became concerned that even though I had benefited from following them, the people I shared them with might not be getting the same results. Not because I was a better developer, but because I had been practicing TDD for a couple of years when I read about the Three Laws and when I tried following them, the habits I had already built filled in the gaps -- which I suspect is also why I didn’t notice the Laws were incomplete at the time.
 
So, what’s missing from the Three Laws and why do I think my version is better? Before I show you my version, let’s start with a reminder. Uncle Bob’s Three Laws go something like this :
  1. You only write production code when there is a failing test.
  2. You write just enough test code for it to fail or fail to compile. 
  3. You write just enough production code to make the failing test pass.
If you compare this description with the three steps of TDD (often referred to as Red, Green, Refactor) you’ll notice that the Three Laws have you moving from Red to Green then back to Red over and over. Though I didn't notice it at first, there is no mention of Refactoring, which is the third step of TDD. Refactoring the code is necessary so we can clean up any mess made while writing the test or production code and make them easier to understand and modify.
 
The other problem I saw is that the Three Laws don’t address the two most common questions developers new to TDD ask; “How do I know what tests to write?” and “How do I know when I’m done?” While you could argue that these questions have nothing to do with the process of TDD, I think that calling them the “Three Laws of TDD” implies they’re all you need to know to do TDD.
 
In any case, I have a zeroth rule that answers the question and, I think, makes the whole thing complete. So, here’s my version of the Rules for succeeding with TDD:
   Burk’s Rules for Test-Drive Development
   0) You only write a test to describe an unmet requirement.
   1) You write just enough test code to fail, and not being runnable counts as failing.
   2) You write just enough production code to make the test runnable.
   3) When the completed test fails for the expected reason, you write just enough production code to make it pass. 
   4) You refactor the production and test code to clean up any mess.

The zeroth rule answers the questions about knowing what tests to write and knowing when you’re done.  It also connects TDD with the system requirements, which means we don’t have to guess about what tests to write and how we know when we’re done.

Once I had developed my solution, I submitted a talk on it to The Developer Conference since their Call-For-Papers was open. The talk was accepted, and I presented it there in June 2021. About six or seven months later, Uncle Bob’s book “Clean Craftsmanship” was published, and I bought a copy.  While reading it, I was surprised and pleased to see that in addition to the Three Laws, he briefly mentioned that there was a Fourth Law - and that it was to Refactor. I don’t know whether he had heard about my talk or not, but I’m glad we agree that refactoring is a critical step to making TDD work.

If you find this useful and/or thought-provoking then please share it with other developers.