Skip to content
Zeger Hendrikse edited this page Aug 5, 2023 · 20 revisions

There is more to TDD than “red, green, refactor”

Although most people have heard of the slogan “red, green, refactor”, it is only a small part of what defines TDD. From its inception, TDD and eXtreme Programing have always been closely knitted.

red-green-refactor

TDD is best understood as a set of skills and heuristics that enable you to guarantee that every line of the code base contributes to the expected behavior while clearly expressing its intent at the same time.

But how do these skills and heuristics then relate to the well-known red-green-refactor slogan that is almost always the first response when people are asked to explain TDD? The short answer is that the aforementioned skills and heuristics are constantly being applied while traversing the red-green-refactor loop.

For example, consider the following skills, heuristics, and principles (to name just a few):

  • Start a test by writing the assertion first → heuristic for the “red” step.
  • The DRY principle (don’t repeat yourself) → principle while refactoring.
  • Refactoring in (extremely) small steps → profound skill in the “refactor” step.
  • Faking and cheating → heuristic to make a test pass, i.e. the “green” step.
  • 0, 1, N → heuristic to generalize production code.

But how on earth are all these skills, principles, and heuristics then connected and practically applied when practicing TDD?

To illustrate this connection, consider the diagram below (inspired by Rob Westgeest). When going through the regular red, green, and refactor phases of TDD, you’ll notice that there are many heuristics, skills, and principles continually being applied. This happens very consciously at first but becomes gradually more ingrained in your way of working as you become more fluent in practicing TDD.

Heuristics

It is exactly this combination of countless skills and heuristics that increases the quality of your code so significantly (as we are effectively building quality in) and hence boosts your confidence to such an extent that you are able to promote your changes to production whenever needed. Perhaps the best and most fun way to practice all of these skills is by participating in so-called coding katas.

Last but not least, TDD is a lot of fun. This repository contains more than enough materials to learn everything about TDD and get up and running in the wink of an eye!

Kent Beck's four rules of simple design

Kent Beck One of the founding fathers of test-driven development: Kent Beck.

Kent Beck introduced the four design rules. After making a test pass, he (strictly) applies the DRY and the reveal intent, hide implementation principles.

Last but not least, he also (again strictly) applies the simplest thing that could possibly work, which is also often referred to as the "fewest elements". This may actually be one of the trickiest practices to apply properly, as you will see.

Design rules

Kent Beck's four design rules are in ascending order of priority: make the test pass, make the code so readable that it immediately reveals its intention, do not allow any code duplication, and always code the simplest thing that could possibly work — Kent Beck

One of the best examples to show the principle that code should express intent is probably a code snippet from one of the possible solutions to the game of life kata (in Java).

public static List<Cell> iterateGameboard(final List<Cell> gameboard) {
  return gameboard
      .stream()
        .map(toDeadCell(which(isLiving, and(), 
            which(hasLessThanTwo(livingNeighboursIn(gameboard)), or(), hasMoreThanThree(livingNeighboursIn(gameboard))))))
        .map(toLivingCell(which(isDead, and(), hasExactlyThree(livingNeighboursIn(gameboard)))))
        .collect(Collectors.toList());
}

It immediately becomes clear why this principle is so important and at the same time, it explains why comments in code are generally speaking redundant.

Uncle Bob's cycles of TDD

Uncle Bob

Doing TDD means you iteratively repeat the following three steps in extremely small increments. This has been captured in what has become best known as Uncle Bob's cycles of TDD:

  1. First we write a failing test. It is important to make the test fail first, as this assures us the test actually works! We are allowed to write just so much test code, that makes the test fail. This includes compilation errors!
  2. Next we implement just enough production code to make the test pass.
  3. We ask ourselves if there is anything that we can refactor, by applying e.g. the DRY principle.

These rules make you switch constantly between the test and production code bases, gradually making the test set more specific and the production code more generic. This is also known as test contra-variance.

By constantly going back and forth between the production and test code, you constantly try to write tests that force the code that is based on the-simplest-thing-that-could-possibly-work more generic. Conversely, in the production code, you try to "trick" the test(s) by implementing just the bare minimum to make them pass.

To summarize, we continuously ping back and forth between our production code and our specifications (test code). We do this in the smallest increments that we can possibly think of. We may want to keep ourselves in check by requiring 100% code coverage at all times. If we get below 100%, apparently we "managed" to write code before we had a test for it. Remember that this spoils the whole idea of TDD!

Clone this wiki locally