TDD or: How I learned to stop worrying and love writing tests

Zeger Hendrikse
NS-Techblog
Published in
8 min readAug 9, 2023

Most of us are probably familiar with the song “Don’t worry, be happy” by Bobby McFerrin. Unfortunately, these lyrics are rarely appropriate to describe the mood of a team of software developers, let alone 100% of the time. It often gets worse when changes are about to be pushed into a production environment.

Does this mean that we can no longer be happy and worry about whether we will be asked to go into production at any given time? Remember that continuous delivery is defined as always being ready to go to production! Is it even possible to be so confident that you are always ready to go to production without worrying about the (latest) changes you or your colleagues have made?

This is why test-driven development (TDD) is so important, as it is one of the key skills needed to achieve continuous delivery. Without it, you cannot be sufficiently confident in the quality of your product. Test-Driven Development ensures that we create high-quality products, as explained by Dave Farley in his video on quality assurance.

Once you have become an experienced TDD practitioner, you have mastered the skills to continuously and confidently bring small changes to production in a fast and sustainable manner. In other words, you’ll even be able to go to production at five o‘clock on Fridays and still start your weekend happily immediately thereafter.

You may be wondering what magic TDD has to offer to make this happen. Fortunately, there is no magic involved. TDD is similar to playing the piano in that it takes deliberate practice to play well. Through deliberate practice, you can attain a level of competence that allows you to play pieces you couldn’t have imagined before.

Photo by Dolo Iglesias on Unsplash

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

So what exactly does TDD involve in order to deliver on all these aforementioned promises?

Although most people have heard of the slogan “red, green, refactor”, this is only a small part of what defines TDD. From its inception, TDD and eXtreme Programing have always been closely linked. TDD is best understood as a set of skills and heuristics that enable you to ensure that every line of the code base contributes to the expected behavior while clearly expressing its intent. The following code snippet gives an excellent example:

Code that clearly expresses its intent: in this case the business rules for the game of life.

But how do these skills and heuristics then relate to the familiar 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 as the red-green-refactor loop is traversed.

To give some concrete examples of what I mean, consider the following skills, heuristics, and principles (to name just a few):

  • Begin a test by writing the assertion first → heuristic for the “red” step.
  • The DRY principle (don’t repeat yourself) → principle for 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 the production code.

But how on earth are all these skills, principles, and heuristics then connected and practically applied when practicing TDD, you may wonder.

To illustrate this connection, consider the diagram below. As you go through the regular red, green, and refactor phases of TDD, you’ll notice that there are many heuristics, skills, and principles that are constantly applied. This is done very consciously at first but gradually becomes more ingrained in your way of working as you become more fluent in practicing TDD. In fact, it is a lot like playing the piano!

Diagram loosely based on an activity during a TDD training by Rob Westgeest.

It is precisely this combination of countless skills and heuristics that so significantly increases the quality of your code (because we effectively build quality in) and thus gives you the confidence to push your changes into production whenever you need to or are asked to.

Practicing TDD with coding katas

Hopefully, at this point, you have a better understanding of what is meant by deliberate practice in the context of TDD. But you may still have some questions: How do I practice these skills? How do I keep practicing TDD fun and feasible when there is so much to learn? It may all seem a bit daunting at first.

Perhaps the best and most fun way to practice all of these skills is by participating in so-called coding katas.

A kata is an exercise in karate where you repeat a form many, many times, making little improvements in each. The intent behind code kata is similar — codekata.com

Like many other topics in the field of software development, the term kata itself comes from martial arts.

Japanese culture influenced a lot software and project management fields. Concepts like Lean, etc has come from Japan. And we should admit, that they have improved the existing processes, increasing efficiency and satisfaction overall — apiumhub.com

Generally speaking, each kata specifically tries to target one or more skills. As the saying goes, practice makes perfect, and the same goes for (coding) katas: the best way to you make them your own is to repeat them over and over again, e.g. with different constraints or in different programming languages.

Fortunately, it turns out that this doesn’t have to be a boring activity as mastery (see Daniel Pink’s Autonomy Mastery Purpose video) is actually one of the three primary drivers that keep us motivated. What’s more, the payoff from mastering TDD is far greater than the initial investment.

Coding dojos

A dojo is a place for immersive learning. Needless to say, coding dojos are designed to eventually master TDD thoroughly, using coding katas as a means. During the coding dojos, TDD practitioners learn, apply, and practice all the aforementioned skills, heuristics, and principles. The coding dojos also introduce practitioners to various aspects of pair programming as well as various mob-programming concepts such as randori, driver-navigator, etc.

During these sessions, we can imagine practitioners going through a series of learning phases, roughly speaking. Starting from an initial skilled practitioner, they gradually move up to a conscious practitioner and may finally eventually reach the skills of a so-called reflective practitioner. As such, the stages are quite similar to the well-known shu-ha-ri-kokoro stages of learning.

During each of these stages of learning, more or less the same set of topics is practiced, but gradually more and more in-depth. For example, such topics include TDD in unfriendly environments, decoupling code from the environment, deliberately improving code in small steps, etc. These stages of learning are depicted in the diagrams below.

An initial draft was created together with Rob Westgeest and has been redrawn in Excalidraw.

A simple coding kata

For example, take the stack kata, made famous by Robert C. Martin, also known as Uncle Bob. But wait, before you watch his screencast, let’s first try to figure out what the first test could look like. And yes, this is even before we have written an empty class called Stack!

Let’s assume that we already have tasked our kata/user story and that we are part of a Python team (of course, the programming language is irrelevant to this exercise). What can we expect from a new stack?

Scrumblr is used as a storyboard and is running on Replit in this repository.

We expect a new stack to be empty, so let’s specify just that:

An empty stack should not contain any elements

You can clearly see how we define the expected behavior and interface of our stack by writing the specification (test) first. Now that we have a failing test, it’s time to move on to our production code. Let’s make this test pass by doing the simplest thing that could possibly work, which is to actually add some code to the stack class that is just enough to make the test pass, taking into account the aforementioned faking and cheating heuristic:

The faking and cheating heuristic applied

And when we see that it actually passes the test, we get our little dose of endorphins! What a difference to the traditional way of coding, where we come to realize that we need to write another unit test after we think we are done.

Since there is nothing to refactor yet, it is time to go back to our specifications and write our next one, which should force us to generalize the production code to do something more useful.

Pushing our first element onto the stack

We run the test and see that it fails. This confirms both that the test actually works and that our assumption is correct (which is trivial in this case because the push method doesn’t exist yet). It is time to jump over to our production code and make the simplest change(s) to make this test pass.

Applying our faking and cheating heuristic once more

Having passed the test, we can and should refactor the code using the DRY principle. Can you spot the duplicate code? Hint: look in the code base of the specification.

Obviously, we cannot go through the whole kata here, but hopefully, this has given you just a glimpse of how coding dojos eventually lead to experienced TDD practitioners by offering coding katas that encourage conscious practice.

Another example of such a coding kata is the Manhattan Distance kata. By generalizing the kata from an initial one-dimensional case to a two-dimensional case to an n-dimensional case, we can practice the 1–2-N principle, also known as three strikes and you refactor: before implementing a generic case N = n, first, implement the case for N = 1, then the case for N = 2, and finally, generalize it to N = n.

References

This post is based on my comprehensive set of TDD training and exercise materials, including katas with instructions, presentations, and more. You can find these materials on GitHub and the wiki pages contained therein.

The code snippet used to illustrate how code can express intent is based on this article on Medium. A TDD version is also included in my GitHub TDD repository.

Finally, many of the ideas presented here were developed in close collaboration with Rob Westgeest of QWAN, which has its own interesting blog.

--

--