Have fun learning about Test-Driven Development with JitterTed's TDD Game

Clarifying the Goal of Behavior Change

Predictive Test-Driven Development: Part 1

Originally published on . Last updated on .

Change Behavior with Predictive TDD

In my last post, I talked about the two main activities of Test-Driven Development[1]:

  1. Change Behavior: adding capabilities and features to a system, or, perhaps, fixing bugs. The goal is improving the lives of the folks that the system serves.

  2. Increase Changeability: refactoring, or re-organizing the code to make it easier for us, the developers, to understand the code and make it easier to Change Behavior (now or later).

We’ll first look at the Change Behavior part of the cycle, often represented by the “Red-Green” parts of the TDD diagram:

TDD Cycle: circles with the words Red (colored red), Green (colored green), and Refactor (colored pale light blue) written inside and arrows pointing from one to the next in a clockwise order. Red and Green circles are highlighted and have text to the right: 'Change Behavior'. Refactor circle has text above it: 'Increase Changeability'.
TDD Cycle: Focus on Behavior Change

When I do (and teach[2]) the Red-Green part of the TDD cycle, the steps I actually perform are more than two:

Flowchart of steps in the process
More Than Just Red-Green

Clarifying Your Goal

Let’s start with the first two steps, which seem simple enough…

First two steps of my TDD process: What Should It Do? and How Will You Know It Did It?
Requires Thinking First

Before I did TDD on a regular basis, it always amazed me how much code I could write without being clear about the behavior I was adding. This became really obvious after I resumed my training work years ago. It was common for students to get stuck, so I’d ask them “what are you trying to do here?” Often, they couldn’t explain it, or did so in a way that was vague. After some back and forth, once they were able to verbalize the goal clearly, all of a sudden they knew what was wrong and how to proceed[3].

Thinking First Helps

Therefore, being clear and precise—in a way that a unit test requires—helps you make sure you know exactly what you need to do. However, that’s not enough for a unit test. You have to be able to observe that the system does what you want it to do, i.e., you have to answer the second question: How Will You Know It Did It?

For example, if you’re thinking about the micro-feature (or “story” if you prefer):

The player gets a new card when they draw one from the deck

How will you know this worked? What, exactly, is the expected (and observable) outcome? Can you access that observation by directly asking an object for information, or do you have to dig into 3 separate objects for details? We’ll need to clarify the assumptions in order to really know.

Let’s start by rephrasing this story in Given-When-Then format[4]:

Given a PLAYER with a HAND containing 2 CARDs,

When the PLAYER DRAWs a CARD from the DECK,

Then the HAND should have 3 CARDs.

Note the use of our DOMAIN terminology: player, hand, card, draw, and deck. Talking, thinking, writing, and discussing the functionality in those terms will not only help us figure out the desired behavior, but make sure we’re using the language correctly with our domain experts.

Now we have precisely defined the behavior we want to add—player draws a card—as well as how we can observe that it happened, by checking that the size of the hand is 3 cards, when it started with 2. (How do we know we started with 2? Sounds like a separate test!)

That’s a lot of work and thinking, but it’s exactly (and only) what we need to get to the next step, which is writing the actual unit test. We’ll look at that in the next part of this series.

The TDD Intro Series

  1. Red-Green or Refactoring First?
  2. Clarifying the Goal of Behavior Change (this article)
  3. Predicting the Failing Test
  4. Implementing the Feature
  5. Tightening Our Assertions

  1. I’ve had people say that the two main activities of TDD are writing a failing test and then making it pass, but that’s only a part of TDD and misses the other equally important activity: refactoring. ↩︎

  2. I use JitterTed’s TDD Game to help teach this process. ↩︎

  3. We often refer to solving problems by talking about them out loud, perhaps to a rubber duck sitting on our monitor, as Rubber Duck Debugging, or sometimes just Rubber Ducking. No human necessary. ↩︎

  4. This way of thinking about the desired behavior comes from Behavior-Driven Development, which, as we’ll see in future posts, is very much TDD, working at different boundaries. ↩︎

Make Your Code More Testable™️

Don't miss out on ways to make your code more testable. Hear about new Refactoring techniques, improving the Test-Driven Development process, Hexagonal Architecture, and much more.

    We respect your privacy. Unsubscribe at any time.