Test-driven development is a way of managing fear during programming.

Kent Beck

We are so ineffably lucky! We’ve had test-driven development for years.

Several decades have passed since the developers who wrote the code for the Mercury Space Program practiced “Punch Card TDD”. XUnit libraries that facilitate the adoption of test-driven development date back to the turn of the century. In fact, Kent Beck, who wrote “Test-Driven Development By Example” and developed the JUnit framework, refers to himself as having “rediscovered” (and not “invented”) the practice of TDD. That statement is evidence of his humility, yet it is also the truth. TDD is as old as software development itself.

Then why is it that test-driven development is still far from the standard way to write code? Why is it often the first practice that gets sacrificed when there is schedule pressure, or when IT budgets need to be trimmed, or (my personal favorite) when there is a desire to “increase the velocity of the software delivery team”? All these reasons are proffered despite the ready availability of empirical and experimental evidence that TDD reduces defect count, creates simpler design, and improves developers’ confidence in their own code.

Why is TDD adopted tentatively and abandoned readily? The following arguments, heard often from those who are reluctant to practice it, may explain the reasons:

  1. I don’t know where and how to start. Perhaps the most common reason is lack of awareness and exposure. Like any other skill, writing code in a test-driven style is something that needs to be learned. Many developers either haven’t had the external inducement (time, resources, guidance, encouragement) or internal motivation (overcoming one’s own reluctance and fear) to learn this skill.

  2. TDD works in toy programs or during coding interviews, but not when writing “real world” code. This is untrue yet understandable. Most test-driven development tutorials and books — including this one — are constrained to pick relatively simple examples from an obvious domain. It’s difficult to write a TDD article or book with actual code from a piece of software plucked from a commercially deployed application (say, from a financial institution, a healthcare management system, or a self-driving automobile). For one thing: much of such real world code is proprietary and is not open-source. For another: it’s the job of the author to show code from a domain that has the widest appeal to the largest audience. It would be illogical, bordering on obscurantism, to show TDD in the context of a highly specialized problem domain. Doing so would require, before anything else, a lengthy explanation of the arcane jargon and cant of that domain. That would defeat the very purpose of the author: making TDD understandable, approachable, even lovable.

    These obstacles to using “real world” code in TDD literature notwithstanding, developers regularly write production software using test-driven development. Perhaps the best and most convincing example is the suite of unit tests for the JUnit framework itself. The Linux Kernel — possibly the most strenuously used piece of software in the world — is being improved with unit tests.

  3. Writing tests after-the-fact is sufficient, TDD is too restrictive and/or pedantic. This is more refreshing to hear than the occasional rant that “unit testing is overrated”! Writing tests after writing production code is an improvement over writing no tests at all. Anything that raises the developers’ confidence in their code, reduces accidental complexity, and provides authentic documentation is a good thing. However, writing unit tests before writing the production code provides a forcing function against creating arbitrarily complexity.

    TDD guides us to simpler design because it provides these two practical rules as guardrails:

    1. Only write production code to fix a failing test.

    2. Refactor energetically when, and only when, tests are green.

Does test-driven development guarantee that all code we ever write will automatically and inevitably be the simplest code that works? No, it does not. No practice, rule, book, or manifesto can do that. It’s up to the people who bring these practices to life to ensure that simplicity is achieved and retained.

This book’s content explains and instructs how test-driven development works in three different programming languages. Its purpose is to instil in developers the habit and self-belief to use TDD as a regular practice. That purpose may be ambitious, but I’m hopeful it isn’t elusive.

What is Test-Driven Development

Test-driven development is a technique for designing and structuring code so that both its simplicity and one’s confidence in it increase in proportion to the size of the code.

Let’s take a look at the various parts of this definition

A technique

Test-driven development is a technique. It’s true that this technique is borne of a set of beliefs about code, viz:

  • That simplicity — the art of maximizing the amount of work not done, is essential. 1

  • That obviousness and clarity are more virtuous than obscurantism and cleverness.

  • That writing uncluttered code is a key component of being successful.

Despite being rooted in these beliefs, as a practical matter, TDD is a technique. Like riding a bike, kneading dough, or solving differential equations — it’s a skill that no one is born with and that everyone has to learn.

Other than this section, this book does not dwell on the belief system behind test-driven Development. It’s assumed that you either subscribe to it already, or that you’re willing to give TDD a try as a new (or forgotten) skill.

The mechanics of that technique — writing a failing unit test first, then briskly writing just enough code to make it pass, and then taking the time to clean up — occupy the bulk of this book. There will be ample opportunity to try this technique for yourself.

In the final analysis, it is more satisfying to learn a skill and also imbue oneself with the beliefs that support it. Just like riding a bike is more enjoyable if you remind yourself that it’s good for your health and the environment!

Designing and structuring code

Notice that TDD is not fundamentally about testing code. It is true that we use unit tests to drive the code, but the purpose of TDD is to improve the design and structure of the code.

This focus is vital. Because if TDD were only about testing, we couldn’t really mount an effective case for writing tests before rather than after the business code is written. It’s the goal of designing better software that moves us; the tests are simply a vehicle for this motion. The unit-tests that we end up with via TDD are an added bonus; the primary benefit is the simplicity of design we get.

How do we achieve this simplicity? It is through the mechanism of RED-GREEN-REFACTOR, which is described in detail at the beginning of Chapter 1.

Enhanced simplicity

Simplicity isn’t a mere esoteric notion. In software, we can measure it. Fewer lines of code per feature, lower cyclomatic complexity, fewer side-effects, smaller run time or memory requirements — any subset of these (or other) requirements can be taken as an objective measure of simplicity.

Test-driven development, by forcing us to craft “the simplest thing that works” (i.e. gets all tests to pass), constantly nudges us towards these metrics of simplicity. We aren’t allowed to add superfluous code “in case we need it” or because “we can see it coming”. We must first write a failing test to justify writing such code. The act of writing the test first acts as a forcing function — compelling us to deal with arbitrary complexity early. If the feature we’re about to develop is ill-defined, or our understanding of it flawed, we’ll find it hard to write a good test up front. This will force us to address these issues before we write a line of production code. This is the virtue of TDD: by exercising the discipline of driving our code through tests, we weed out arbitrary complexity at every juncture.

This virtue isn’t mystical: using test-driven development won’t cut down your development time, the lines of code, or defect count by half. What it will allow you to do is to arrest the temptation to introduce artificial and contrived complexity. The resultant code — driven by the discipline of writing failing tests first — will emerge as the most straightforward way to gets the job done, i.e. the simplest code that meets the needs of the tests.

Increased confidence

Code should inspire confidence, especially code we have authored ourselves. This confidence, while itself a nebulous feeling, is grounded in an expectation of predictability. We are confident in things whose behavior we can presage. If the corner coffee shop under-charges me one day and over-charges me by the same amount the next day, I’m likely to lose confidence in them even though I break even over the two days. It’s a nature of human psychology that we value regularity and predictability even higher than net value. The world’s luckiest gambler, who may have just won ten times in a row at a roulette table, wouldn’t say that they “trust” or have “confidence” in the wheel. Our affinity for predictability survives even dumb luck.

Test-driven development increases our confidence in our code because each new test flexes the system in new and previously untested ways — literally! Over time, the suite of tests we create guard us against regression failures.

This steadily growing battery of tests is the reason that the code’s quality and our confidence in it grow in proportion to the size of the code.

Who is this book for

This is a book for developers — people who write software.

There are many professional titles that go with this vocation: “software engineer”, “application architect”, “devops engineer”, “test automation engineer”, “programmer”, “hacker”, “code whisperer”, and countless others. Titles may be impressive or humble, trendy or solemn, traditional or modern. However, the one thing that’s common in the people professing these titles is this: they spend at least a part of their week — if not each day — in front of a computer, reading and/or writing source code.

I have chosen the term “developers” to represent this community of which I’m both a humble and grateful member.

Writing code is one of the most liberating and egalitarian activities one may imagine. In theory, all one needs by way of physical prowess is the possession of a brain. Age, gender, sex, nationality, national origin — none of these should be a barrier. Having physical disabilities shouldn’t be a barrier.

However, it would be naive to assume that reality is as neat or fair as that. Access to computing resources isn’t equitable. A certain level of wealth, freedom from want, and security are necessary. Access is thwarted even further by badly written software, badly designed hardware, and myriad other usability limitations that prevent all people from learning to program based solely on their interest and effort.

I have tried to make this book accessible to as many people as possible. In particular, I’ve tried to make it approachable to people with physical disabilities. The images have alt-text to facilitate e-reading. The code is available via GitHub. And the prose is simple and straightforward.

In terms of experience, this book is intended both for people who are still learning how to program and for those who already know how to program. If you are ramping up on one (or more) of the three languages in this book, you are well within the target audience.

However, this book does not teach the basics of programming in any language, including Go, JavaScript, or Python. The ability to read and write code in at least one of the programming languages is a requirement. If you are absolutely new to programming, it’d be wise to solidify the foundations of writing code in one of the three languages before you proceed with this book.

The sweet spot for this book spans developers who are beyond their early forays into programming all the way to seasoned architects, as shown in figure P11. (Kent Beck is an outlier.)

This book is for software developers
Figure P-1. This is a book for software developers

Writing code can be, by turns, exhilarating and exasperating. However, even at its most frustrating, there should always be more than a glimmer of optimism and a bushel of confidence that we can make code do our bidding. With perseverance, you’ll find that your journey through this book is fruitful and that the joy of writing code in a test-driven manner is one you want to savor long after you’re done reading the Reflections chapter.

What are the prerequisites for reading this book

By way of equipment and technical prowess, you should:

  • Have access to a computer with Internet connectivity.

  • Be able to install and delete software on that computer. That is, your access on that computer should not be restricted; in most cases this would require having “Administrator” or “Superuser” access on that computer.

  • Be able to launch and use a shell program, a web browser, a text editor, and (optionally) an Integrated Development Environment on that computer.

  • Have installed (or can install) the run-time tools for one of the languages used in this book.

  • Be able to write and run a simple program — “hello world" — in one of the languages used in this book.

The “Setting up your development environment” section in Chapter 0 - Introduction & Setup has more installation details.

How to read this book

The subject matter of this book is “how to do test-driven Development in Go, JavaScript, and Python”. While the concepts discussed are applicable to all three languages, the treatment of each language necessitates some separation of the material in each chapter. The best way to learn test-driven Development (like any other acquired skill) is through practice. I encourage you to both read the text and write the code on your own. I call this style "following the book" — because it includes active reading and active coding.


To get the most out of this book, write the code for the Money example in all three languages.

Most of the chapters have general-purpose sections that are applicable to all three languages. These are followed by language-specific sections, where the code for one of the three languages is described and developed. These language-specific sections are always clearly marked by their headings: Go, JavaScript, or Python. At the end of each chapter are one or two sections that summarize what we have accomplished thus far and what comes next.

Chapter 5 through Chapter 7 are unique insofar as they deal exclusively with one of the three languages: Go, JavaScript, and Python, respectively.

Figure P-2 shows a flowchart describing the layout of this book and the different ways to follow it.

How to read this book
Figure P-2. Flowchart on how to read this book

Here are some “reading pathways” on how to best follow this book.

Follow the book one language at a time

I recommend this pathway if one or more of these conditions apply to you:

  1. I am aching to dive into one of these languages before tackling the other two.

  2. I’m particularly curious (or skeptical!) how TDD works in one of the three languages.

  3. I learn best by working in one language at a time, rather than multiple languages simultaneously.

Follow the flow chart shown in figure P-2 one line at a time. For example: if you are eager to learn TDD in Go first, skip the sections marked JavaScript and Python in the first reading. Then do a second pass through the book for Javascript, and a third to finish things off in Python. Or you may do the languages in a different order. The second and third pass should be quicker than the first, however, be prepared for the unique quirks of each language!

If you follow the book this way, you will find that writing the code successively in each language gives you greater insight on TDD as a principle — beyond the details of testing as a language feature. Getting into the habit of writing tests is necessary; however, understanding the reasons for why test-driven development works across languages is even more important.

Follow the book in two languages first and then in the third language

I recommend this pathway if you identify with any of the following statements:

  1. I want to build and compare the solutions to the same problem in two languages.

  2. I am less comfortable with one of the languages and want to defer it after the other two.

  3. I can code in two languages at a time but would find it difficult to juggle all three at once.

Follow the flow chart shown in figure P-2 two lines at a time. After you’re done with following the Money problem for two languages, do a second pass through the book to follow the third language.

It can happen that you want to follow two languages in the first pass yet cannot decide which language to defer to a second reading. Here are some suggestions on how to pick two out of the three languages:

  1. Want to contrast a dynamically-typed language with a statically typed one and want to keep the language tech stack simple? Follow Go and Python first and JavaScript next.

  2. Ready to learn contrasting ways in how to build code in two different languages and ready to tackle tech stack variations? Follow Go and JavaScript first and Python later.

  3. Want to compare and contrast two dynamically-typed languages? Follow JavaScript and Python first and Go next.

If you read the book this way, you’ll quickly discover the similarities and differences of doing TDD in multiple languages. While the syntactical and design variations in the languages create obvious differences; you may be surprised by how deeply the discipline of TDD permeates into how you write code, regardless of the language in which you write code.

Follow the book in all three languages simultaneously

I recommend this pathway if any of these statements resonate with you:

  1. Want to gain the best value by learning the contrasts and similarities of the three languages.

  2. Find it easier to read a book from start to finish instead of doing multiple passes through it.

  3. Have some experience in all three languages but haven’t practiced TDD in any of them.

If you can write code in three languages simultaneously without getting overwhelmed, I recommend this pathway.

Regardless of the pathway you choose, be mindful that when you’re writing code, you will likely face challenges that have to do with your specific development environment. While the code in this book has been tested for correctness (and its continuous integration build is green), that does not mean it will work on your computer at first go. (On the contrary, I can almost guarantee that you will find interestingly steep portions on the learning curve.) One of the key benefits of TDD is that you control the speed at which you proceed. When you get stuck, slow down. If you make progress in smaller increments, it is easier to find where the code went astray. Writing software means dealing with errant dependencies, unreliable network connections, quirky tools, and the thousand natural shocks that code is heir to. Slow down when you feel overwhelmed: make your changes smaller and discrete. Remember: TDD is a way of managing fear of writing code!

Conventions used in this book

There are two categories of conventions used in this book that require explanation: typographical and contextual.

Typographical conventions

The prose in this book is in the font-type used in this sentence. It is meant to be read and not entered verbatim as code. When there are words used in prose that are also used in code — such as class, interface, or Exception — a fixed width font is used. This alerts you that the term is or will be used — spelled exactly the same way — in code.

Longer segments of code are separated into their own blocks, as shown below.

package main

import "fmt"


func main() {
    fmt.Println("hello world")

Ellipses mean irrelevant code or output has been omitted

Everything in a code block is either something you type in verbatim or something the program produces as the literal output — with two exceptions.

  1. Within code blocks, ellipses (...) are used to indicate either omitted code or omitted output. In both cases, whatever is omitted is irrelevant to the current topic. You should not type these ellipses in code, or expect to see them in the output. An example is shown in the code block above.

  2. Within code blocks that show output, there can be ephemeral values — memory addresses, timestamps, elapsed time, line numbers, auto-generated filenames, etc. — that will almost certainly be different for you. When reading such output, you may safely ignore the specific ephemeral values, such as the memory addresses in the following block:

AssertionError: <money.Money object at 0x10417b2e0> !=
                <money.Money object at 0x10417b400>

Tips are suggestions that can be helpful to you while you write code. They are separated from the main text for easy reference.


Important information that is vital to the topic is identified like this. Often there are hyperlinks or footnotes to resources that provide more information on the subject.

In most chapters, there is extended development and discussion of code in each of the three languages. (The exceptions are Chapters 5, 6, and 7, which deal exclusively with Go, JavaScript, and Python respectively.) To separate the discussion of each language, a heading and an icon in the margin indicate the language that’s the exclusive purview of that section. Keep your eyes peeled for these three headings and icons:

  • Go Go

  • JavaScript JavaScript

  • Python Python

Lexical conventions

This book discusses core software concepts and backs it up with code in three different languages. The languages are sufficiently different in their individual terminology so as to present challenges when discussing common concepts.

For example: Go does not have classes or class-based inheritance. JavaScript’s type system has prototype-based objects — which means that everything is really an object, including things typically thought of as “classes”. Python, as used in this book, has the more “traditional” class-based objects. 2. A sentence like “We will create a new class named Money.” isn’t merely confusing, it’s downright incorrect when interpreted in the context of Go.

To reduce the potential for confusion, I’ve adopted the general terminology shown in Table P-1 to refer to key concepts in a programming-language-agnostic fashion.

Table P-1. General terminology used in this book
Term Meaning Equivalent in Go Equivalent in JavaScript Equivalent in Python


A singular, independently meaningful domain concept; a key noun

Struct type




An instance of an Entity; a reified noun

Struct instance




A sequential list of Objects of dynamic length





A set of (key, value) pairs, where both keys and values can be arbitrary Objects and no two keys can be the same





A set of operations with a given name; functions may (or may not) have entities has both inputs and outputs, but they’re not associated directly with any one Entity





A Function that is associated with an Entity. A method is said to be “called on” an instance of that Entity (i.e. an Object)




Signal an error

Mechanism by which a Function or Method indicates failure

Error return value (conventionally, the last return value of a function/method)

Throw an exception

Raise an exception

The goal is to use terms that explain the concepts without favoring one programming language’s terminology over others. After all, the biggest take-away from this book ought to be that test-driven development is a discipline that can be practiced in any programming language.

In sections of the book that deal with one of the three languages (and are clearly marked in the heading), the text uses the language specific terms. For example, in a Go section, there will be instructions to “define a new struct named Money.” The context makes it clear that this instruction is specific to a particular language.

Using Code Examples

The source code for this book is available at

If you have a technical question or a problem using the code examples, please send email to .

This book is here to help you learn and practice that art of test-driven development. In general, you may use any code provided in this book in your programs and documentation. You do not need to contact us for permission unless you’re reproducing a significant portion of the code. For example, writing a program that uses several chunks of code from this book does not require permission. Selling or distributing examples from O’Reilly books does require permission. Answering a question by citing this book and quoting example code does not require permission. Incorporating a significant amount of example code from this book into your product’s documentation does require permission.

We appreciate, but generally do not require, attribution. An attribution usually includes the title, author, publisher, and ISBN. For example: “Learning Test-Driven Development by Saleem Siddiqui (O’Reilly). Copyright 2022 Saleem Siddiqui, 978-1-098-10647-8.”

If you feel your use of code examples falls outside fair use or the permission given above, feel free to contact us at .

The Apologies

Merriam Webster’s dictionary leads with two definitions of the word “apology”, shown in figure P-3.

An apology can be either an admission of guilt or a defense of criticism
Figure P-3. An apology — either a mea culpa or a defense

An apology is either an expression of contrition, or a pre-emptive clarification against criticism. This section leans towards the second meaning. However, I acknowledge that it may also satisfy the first definition!

Why does this book use Go, JavaScript, and Python?

This book uses Go, JavaScript, and Python as the three languages with which to demonstrate the practice of test-driven development. It’s a fair question: why these three languages?

Here are some reasons.

1. Variety

The three languages in this book represent a diversity of design options, as shown in the table P-2.

Table P-2. Comparison of Go, JavaScript, and Python
Feature Go JavaScript Python


“Yes and no”

Yes (as an compliant language)


Static vs. Dynamic Types

Statically typed

Dynamically typed

Dynamically typed

Explicit vs. Implicit Types

Mostly explicit, variable types can be implicit

Implicitly typed

Implicitly typed

Automatic type coercion

No type coercion

Partial type coercion (for Boolean, Number, String, Object). No coercion for arbitrary class types

Some implicit type-coercion (e.g. 0 and "" are `false`y

Exception mechanism

By convention: second return type of methods is error, caller must explicitly check if this is non-nil

throw to signal an Exception and try ... catch to respond to it

raise to signal an Exception and try ... except to respond to it


Not yet!

Not needed due to dynamic typing

Not needed due to dynamic typing

Testing support

Part of language (in "testing" package and with go test command)

Not part of language, many libraries available (e.g. Jasmine, Mocha, Jest)

unittest library is part of language

2. Popularity

Python, JavaScript, and Go are the top three new languages that developers want to learn as found in several annual surveys by Stack Overflow in 2017, 2018, 2019, and 2020. Figure P-4 shows the result of the 2020 survey.

Python, JavaScript, and Go rank as the top three languages developers want to learn
Figure P-4. Most desirable new languages to learn in a survey of developers by StackOverflow

3. A personal reason

Over the last four years or so, I had the opportunity to work on several projects where the tech stack featured one of these three as the primary programming language. While working with other developers I found that in general, their eagerness to learn and practice TDD was evenly matched by their inability to find resources (or muster the discipline) to do so. They wanted to practice TDD, but didn’t know how or couldn’t find the time for it. Tellingly, this was true for both seasoned developers and “noobs”.

I hope this book serves as both a practical guide and a source of inspiration to those who want to learn and practice TDD in any language; not just in Go, JavaScript, or Python.

Why not this other language?

For starters: there is a vast number of programming languages. One could conceivably write half a dozen books like this and still cover only a small fraction of the languages that developers over the world use on a daily basis to write code for academic, business, and recreation purposes. 3

Besides, there is an excellent book already available for test-driven development in Java. Kent Beck’s seminal work is what inspired me, as it did countless other developers to fall in love with the art and science of TDD. It also begot the “money problem” that’s a major theme in this book.

I am sure there are many other languages for which a practical TDD guide would be beneficial. How about R? Or SQL? Or even COBOL?

Let me assure you: the reference to COBOL was neither a straw man argument nor a cheap shot. In the mid 2000’s, I worked on a project where I demonstrated the ability to do TDD in COBOL using COBOLUnit. It was the most fun I’ve had in a language that’s more than a decade older than I am!

I’m hoping that you will pick up the mantle. That you will learn, teach, and espouse the skills and discipline necessary to practice test-driven development in other languages. That you will write a blog, an open-source project, or the next book in this series.

Why does this book have a “Chapter 0”?

The vast majority of programming languages use 0-based indexing for arrays and other countable lists. 4 This is certainly true for the three programming languages that form the basis of this book. In one sense, this book honors the rich history of programming culture by numbering the chapters starting at 0.

As another homage: the number Zero is itself a radical idea. Charles Seife has written a whole book on this lonely number. In tracing the history of Zero, Seife notes the reservations that the Greeks had about a number that represents nothing:

In that [i.e. the Greek] universe there is no such thing as nothing. There is no zero. Because of this, the West could not accept zero for nearly two millennia. The consequences were dire. Zero’s absence would stunt the growth of mathematics, stifle innovation in science, and, incidentally, make a mess of the calendar. Before they could accept zero, philosophers in the West would have to destroy their universe.

Charles Seife, “Zero - the biography of a dangerous idea”

At the risk of getting too sublime: test-driven development occupies a similar place in programming culture today as Zero did in Western philosophy a few millennia ago. There is a resistance to adopting it, born of a strange combination of dismissiveness, unease, and a belief that it’s just too much fussiness about nothing. “Why should I be fastidious about writing tests first — I already know how I’m going to code the feature!” “Test-driven development is pedantic: it only works in theory and never in practice.” “Writing tests after you’re done writing production code is at least as effective as, if not more than, writing tests first”. These and other objections to TDD make it resembles the number “0” in how radical it is!

The practice of a book having a Chapter Zero isn’t entirely radical, anyway. Carol Schumacher has written an entire book titled “Chapter Zero: Fundamental Notions of Abstract Mathematics” which is a standard textbook for Advanced Mathematics in many college-level curricula. No prizes for guessing what numbered chapter that book begins with!

Dr. Schumacher, in the Instructor’s Manual for her book, says something that I have found illuminating:

Your task as a writer is to give the right cues to your readers, cues that will make it as easy as possible for them to understand what you are trying to say.

Carol Schumacher, Instructor’s Resource Manual for use with Chapter Zero

I have taken this advice to heart. Pragmatically, a title containing “0” helps to set Chapter Zero apart from the treatise that follows it. Chapter 1 of this book puts us on a TDD journey which carries on through the next dozen chapters. Chapter Zero is about describing what that journey is, what we need to know and have before we embark on it, and what to expect when we are on it.

With that last apology out of the way, let’s move right on to Chapter Zero!

1 This definition of simplicity is enshrined in one of the twelve principles behind the Agile Manifesto.

2 Python is very fluid in its support for Object Oriented Programming. For example, see which implements prototype-based objects in Python:

3 Although a book about TDD on a certain language is unlikely to get approval from publishers. Its name starts with “Brain” and ends with an expletive!

4 Lua is a notable exception. My friend Kent Spillner once gave a fascinating talk on this subject, which I summarized here

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.