Test-Driven Development

Steven J Zeil

Last modified: Dec 27, 2023
Contents:

Abstract

We’ve seen how Test-First Development (TFD) moves unit testing to the forefront of the code-writing process.

Test-Driven Development (TDD) is an enhanced version of TFD designed for use with incremental development. The emphasis is on quick cycling between testing and implementation, with each cycle typically a matter of a few minutes.

1 Test-Driven Development

Test-Driven Development (TDD) is a stronger form of Test-First Development.

In TDD, we repeatedly:

  1. Write just enough of an automated test case for a new desired behavior for the test to fail.

    • This case must, initially, fail.
    • Not compiling counts as “failing”.
  2. Write just enough new code to pass the test.

  3. Refactor the code to make it acceptable quality.

This ties in very nicely with some of our previous discussion of incremental development. In particular, compare to the way we break stories into tasks.

1.1 The “Three Rules of TDD”

From Robert Martin

Over the years I have come to describe Test Driven Development in terms of three simple rules. They are:

  1. You are not allowed to write any production code unless it is to make a failing unit test pass.
  2. You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.
  3. You are not allowed to write any more production code than is sufficient to pass the one failing unit test.

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

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

2 TDD during Incremental Development

We can’t use the same tasks for TDD that we used for TFD. Look again at the tasks we had for TFD:

  1. Create/modify the API to describe a new desired behavior.
  2. Write the unit tests.
  3. Implement the new behavior.
  4. Integrate and commit changes.

Perhaps you can see the problem. TDD does not treat steps 1, 2, & 3 above as separate tasks. Instead, it combines them into a single iterative process in which we repeatedly do a little bit of #2, then a little bit of #3, discovering the API (#1) along the way.

So, for TDD, my task breakdown is considerably shorter:

  1. Implement the story. (includes unit testing)
  2. Integrate and commit changes.

3 Case Study 1: TDD of a Spreadsheet Story

Read Martin’s own example of TDD, the Bowling Game Kata.

4 Case Study 2: TDD of a Spreadsheet Story

Story: As a spreadsheet designer, I would like to write expressions involving square roots

Refer to the outline for the video of the case study illustrating TFD.