Introducing TDD is a lot more psychological than you think.
When we start introducing TDD we need to think about how people new will react. For example, the first question that people ask is:
How am I going to write the test if I don’t know what the code does?
For people who are already doing TDD the question seems strange, but let’s dig deeper. There’s a lot we can learn from this question.
It is asked because the person can’t see another option, besides code-first and test-later. This is how we are taught to write software. In fact, people who are being introduced to unit testing (in test-after fashion), see writing the tests as an additional step to what they are already doing now. First, we solve the problem, then we make sure it works. Spock would approve.
Test-after seems so logical in how we write, flipping the order seems radical. Not only that, when they are exposed to TDD for the first time, people don’t yet understand the additional value of test-first. It seems like a technical change – swapping the order of operations.
And if the change is only technical, the question seems viable. How can I test it, if I don’t think of it first?
The answer is, of course, you don’t.
Which is very confusing.
TDD has many benefits. One of them is making sure you are solving the right problem. In order to do that, the code that you’re going to write is going to be used, either directly by the user, or mostly by some other code. It will need an interface, different inputs and a way to convey the result.
It’s not that we don’t think about these when we jump into the code first. We just add them, when we find we need them. But we think of the implementation first.
We don’t think about a simplified interface, because, as with tests, we think we can patch it on later. The code that we write is a catch-all, because we’re putting all the cases we can think of in there. This is the small beginning of the big ball of mud.
In TDD, we need to think about interface first, and implementation later. How the code will be used, rather than how it’s built. We need to make progress in very small steps, slicing the functionality, to move in increments, knowing all things still work.
These are not things we are used to do.
When we’re talking about motivation for unit testing, we want to know if the code works. In TDD we shift the need to know how it will be used. Once we answer that question, the solution is not only implementation details, but also a much simpler one than we’d probably write code-first.
As with all traps, it is better to avoid the “how to test code that doesn’t exist” conundrum altogether. Instead of starting with the process of TDD, which we often do, start the discussion about how we write solutions that work. TDD helps us a lot in writing those, and makes sure they do what we want.
After all, the process is just implementation details.