Unless one is a in very experimental time for a project one should write tests first to write the code to actually do something.
By doing so, in the end, tests are just a step on the way to write code, not an added cumbersome step.
Nicely expressed in a series of Twitter threads by @allenholub at the end of December 2022 this approach is one many developers don't get.
- "The purpose of TDD is to design/build the code, not test it"
- "When doing TDD, I just look at the red/green. The message is irrelevant [...] if it's red, I immediatly 'git restore'. It's easier to rewrite the last minute's work than find the problem"
- "[...] The 'tests' exist to make the refactoring safe. Any useable tests that emerge are a residual side effect" as a rebound to @tottinge's tweet.
- a complimentary thread about how to use TDD in a legacy code base
Let's unwrap this.
Tests are not the burden you think
Many teams I have worked with initially saw tests as an extra burden for them. I have lost count how many times I have heard "I have written feature X, I still need to add the tests now but the work is done" during a daily standup.
In turn, management and product people, hearing that "tests" are an after though will often ask if they are truely needed. After all, if the work is already done before we add the tests ... why shouldn't we skip that step?
The short sighted answer to that question, or a way to frame tests, is that tests are a kind of security belt to avoid loss of features or additional bugs in the future. "If we have tests for that, we will know if any change in the code base breaks that piece of code". It's true, and I have said it myself many times. But it's not the actual point.
This is probably the biggest issue developers face with tests: reframing their purpose from a security belt to a design tool.
Tests are a design tool
Once you know where you want to go you can split that destination, that vision in several steps starting with the most immediate and only possible one. This is the first test to write. The first that will be, inevitably, red.
You then have a clear expression of a requirement and once the code is written if the requirement is fulfilled.
Those steps should be small, infused with the design patterns and coding way of your team. The way you write the tests should be an expression of those patterns and coding ways. So that the code you will write to turn them green will, inevitably, also be alongside those lines.
This implies your team has come with those patterns and coding ways. It also implies you have taken enough steps back to consider what you are trying to solve and are able to express, at least, the first, immediate steps for it.
Step back first, then write the first test
Tests are a design tool, but to design you need to understand the problem you are trying to solve. In other words, you need to see your destination. Then you can figure out what is the first step you need to take to get there.
So, you first need to take a step back. Look up and around at the issue you are trying to solve, the feature you are trying to add, within the context of the code base and the project. Understand why this is happening, where we are headed. Figure out the destination, then the first step.
This first step should be expressable in a first test. A simple, one liner test.
Then turn it green. Then refactor to match style and performance requirements.
Then start again from the top.
Don't just stack stones
Stacking stones on top of each other might indeed build you a pyramid but it won't get as high as one you carefully planed for. Or worse, the project might have been to build a wall but as you just rushed into stacking stones you totally missed the end goal and now you have to do it again.
So, TDD is not about writing tests, it's about writing code. It's about figuring out the destination and crafting out the journey one step at a time. It's about checking where we need to be at the end of the next step and then making sure we reach that place.
What's important is to reach the journey properly. At the end the tests are still there to show we did the work well and ensure we don't get lost on the next journey (refactoring, more features, ...).
What about you?
Any of those points ringing a bell to you? Is your team struggling to adopt TDD and tests are still a burden for most? We should have a chat about it!
You can book a free 30 minutes chat by video or just audio call to talk about tests, TDD. You don't have to be using Ruby as you main language and we will see where things take us from there.