TDD: First Step is THINK
Tags: tdd, programming
Published: 2022-11-02 20:36:17

When you read about or hear about Test Driven Development, the first thing folks are always told is that in a TDD cycle, the first step is **Red**.  You want a test that after it can compile, fails for the **right reason**.

What you are never told is HOW to get to Red.  The #1 question I get from newbies trying to learn TDD, and even seasoned practitioners can struggle with this, is **what test should I write?**

Therein lies the first step in TDD.  It's not red, it's **Think**!  This is <u>how</u> to get there.  It requires us to slow down and to stop rushing.

### So...how do we <u>think</u>?

---


# TDD is about the Journey
Before we talk about how, remember that Test Driven Development is really a journey guided by test driven <u>design</u>.  It's evolving your design in order to provide some small piece of value to your end user.

Every TDD cycle moves us closer to completing the journey.  Moving deliberately and safely as we travel thoughtfully through the codebase at a sustainable pace.

# Meet Your Guide: A Combination of Dialog, Describes & Tests
**We have stuff to deliver, we don't want to have to spend all day to read the code.**

As a developer first and foremost you are your own customer. Collective Code Ownership requires that we can all read the code and lay of the land quickly and easily.  

_You_ the developers are the ones reading all the tests and the production code over and over again day in and day out.  We spend more time reading the code than writing it.

Therefore writing well-named tests and production code is the one of the key attributes of a codebase that helps to enable true Agility and allows you to pivot quickly as requirements change.  And we take this for granted far too often.

In order to write tests that document what you're about to TDD, you must have conversations with your team and the business and each other early and often.  You must learn the customer's business domain which is part of THINK, the first step in TDD.

Then, writing a combination of test describes and underlying tests, you eventually finish that journey and push that small piece of value to production.

# Don't Jump the Gun
When we work in a TDD workflow, we often feel like jumping the gun, starting inside the test trying to implement details of the test.  We prematurely start thinking about how the public contract should change in the production code that is under test.

What we should do instead is to stop, back up, and THINK first a bit about what we're trying to do next.  We do this initial thinking up front, through our **test name**.

By doing so, we can:

- uncover misconceptions about requirements and the WHY behind the value we intend to test drive
- discover a smaller, safer step we might take in order to get faster feedback
- uncover more domain terminology up front
- prevent yourself from getting into rabbit holes throughout the journey
- help guild the wishful thinking portion (implementation) of our test

# Creating Test Names Guides Your Overall Journey
Writing tests with pure behavior can and should be done at any layer in your application.  Even down to the lowest level unit test.

Once we have a test name, then it can guide our test's implementation using wishful thinking.  Our wishful thinking always reverts back to our test name.  We can then come up with a more straight forward path to implementing the behavior we desire.

Good test names are written via well written prose.  They are ignorant of the how.  They contain domain terms, not implementation details.

But how can we come up with these?

## Coming up with a Test Name
Here are a few steps you can use to come up with that next test name:

**√ Do you have a _small_ scenario you're trying to test drive?**  

If not, come up with one by having conversations with the business, your pair, testers, designers, PMs, or whoever.  This is the first step.  If you do not have this, then literally **what** are you doing next and **why**? This should be a very bad itch for you that you should take care of _now_.

**√ Are you stuck for words in your test name?**

Write out some domain terms that might make sense.  Don't know them?  Good, this is a hint that it's time to discover a little more.  Take a few minutes and THINK.  Talk to your pair, pull in a business stakeholder.  We're not asking for an all day session, just a few minutes.

**√ Look at the last test you just wrote**

Think about what the smallest step next will be to inch you closer to completing that scenario.

**√ Perhaps your previous test was too big of a step?**

The previous test was too big to begin with, and that is a code smell.  You are left with no ideas because that previous test did / covered so much behavior already and you probably realize now that you should have taken smaller steps by writing smaller tests next time.

**√ Try creating a** [CRC card](http://wiki.c2.com/?CrcCard)
Creating a CRC card with your pair (or just you) is diving into implementation (wishful thinking) a bit first, but if you're really stuck, it might help you think about domain terms.  Once you have a CRC card created, then use those terms in it to help create your test name, before implementing the test.  You do not need to be in a class oriented language to utilize these.

**😭 Ah! I give up, I am simply lost!**

If you are totally lost, tired, etc. then go back to your roots.  Look at the scenario.  Look at the describe under which you are writing tests for.  The describe should describe that scenario.  Again, look at the previous test you just wrote. That might trigger thoughts again as to where you might want to go next.

**🥳🍻 Perhaps there are no more tests to write**

You literally cannot think of another test, and you realize that the scenario you are test driving for is working!  It _is_ done! Done enough!  Don't over think it.  Push this to production, lets get feedback.

## What Good Test & Describe Names Look Like
It's not enough to talk about test names, lets see some examples of real-world test names that describe behavior and domain, not implementation details.

original: `describe("ViewCompanyPage"...`
better: **`describe("View Company"...`**

original: `describe("<ConfirmationModal />"...`
better: **`describe("Add Application Confirmation"...`**

original: `sets isStoringToAWS to true when saveToAWS call is in progress`
better: **`sets storing status to in progress when saving a project`**

original: `sets aws status to in progress when saving to aws`
better: **`sets status to in progress when saving company's list of locations`**

original: `shows error modal when saving application fails`
better: **`presents an error call to action when saving an application fails`**

original: `should render fallback banner if that field is undefined`
better: **`shows a fallback banner when none exists`**

Original: `should display an Add a Requirement button`
better: **`displays an add a requirement option`**

original: `describe("when currentStep is the last step"...`
	`it("should display an Add a Requirement button"...`
better: **`describe("When current step is the last step"...`
	`it("displays an add a requirement option"...`**

original: `should display Launch button as disabled when disableNext is true`
better: **`shows a disabled launch option`**

original: `displays introductory text on the page`
better: `displays introductory text`

original: `renders no cards when there are no projects`
better: **`shows no projects when there are none`**

original: `should display 404 when there no application is found`
better: **`displays an error page with copy`**

original: `should display an input field for description`
better: **`displays a field for specifying a description`**

original: `numberOfApplications renders kinds of applications on the page`
better: **`indicates how many of each kind of application there are`**

original: `calls the graphql endpoint and maps the program data from AWS`
better: **`retrieves a list of programs`**

Given/When/Then
original: `when submitting a form`
better: **`when submitting an application`**

original: `should call setForm data with user input when form is submitted`
better: **`sets user data when submitted`**

**Notice these test names above do not contain:**
🚫 delivery mechanism (form, page, modal, button, http, api, etc.)
🚫 function names
🚫 DOM tags
🚫 library or tech terms


They instead describe pure behavior independent of how.

**Here is a list of various implementation details we removed:** "form", "AWS", "isSavingToAWS", "saveToAWS", "modal", "button", "page", "null", "input" (when referring to type of DOM tag), "numberOfApplications", "ViewCompanyPage", "cards" (from a UI framework), "disableNext", "404", "setForm"

**Domain terms used:** "program", "company", "application", "application confirmation"

### By writing test names like this first it:

1️⃣ Helps guide you in thinking about domain language as you implement the test with wishful thinking

2️⃣ Decouples your test name from implementation details

👉 If implementation (the how) changes, or delivery mechanism changes, the test name should not care and still represent valid behavior under test. You should not have to go and rename your test every time your implementation details change.  If you do, your tests are coupled and that's a smell.

3️⃣ Acts as human readable documentation that tells part of the story of your codebase, the slice of value that was delivered in this part of the application

👉 Being able to read a describe and its underlying test names should be like talking to a person, where your eyes do not have to trip up or over technical terms to decipher the meaning of the test.  You should be able to read a describe and test names within seconds and know what that area of the app is already doing