This post examines how well we really understand the practice of Test-Driven Development (TDD).
Red, Green, Refactor
By now we all know that Test-Driven Development (TDD) follows a simple cycle consisting of these steps:
- Start by writing a test. Since there is no code, it will fail (Red)
- Write just enough code to make the test pass (Green)
- Clean up the code (Refactor)
The beauty of this division is that we can focus on one thing at a time.
Specify, Transform, Refactor
For instance, after doing TDD for a while we may look at the steps as:
- Specify new required functionality
- Improve the functionality while keeping the design constant
- Improve the design while keeping the functionality constant
When we look at the TDD cycle in this light, we see that the Green and Refactor phases are each others opposite.
Refactorings and Transformations
Refactorings are standard alterations of the code that change its internal structure without changing its external behavior. Now, if the Green and Refactor phases are each others opposite, then you might think that there are “opposite refactorings” as well. You would be right. Robert Martin‘s transformations are standard alterations of the code that change its external behavior without changing its internal structure.
Most of us use powerful IDEs to write our code. These IDEs support refactorings, which means that they can do the code alteration for you in a manner that is guaranteed to be safe. So do we need something similar for transformations? I think not. Some transformations are so simple in terms of the changes to code, that it wouldn’t actually save any effort to automate them. I don’t see a lot of room for improving the change from
while, for instance.
Other transformations simply have an unspecified effect. For example, how would you automate the
The crux is that refactorings keep the external behavior the same, and the tools depend on that to properly implement the refactorings. However, transformations don’t share that property.
In the Specify/Transform/Refactor view of TDD, we write our programs by alternating between adding tests, applying transformations, and applying refactorings. In other words, if we look at the evolution of our non-test code through a series of diffs, then each diff shows either a transformation or a refactoring. It seems we are getting closer to the Lean principle of Standardized Work. What’s still missing, however, is a deeper insight into the Red/Specify phase.
How to Write Tests
The essential part of the Red/Specify phase is obviously to write a test. But how do we do that? For starters, how do we select the next test to implement?
There is almost always more than one test to write for a given requirement. And the order in which you introduce tests makes a difference for the implementation. But there is very little advice on how to pick the next test, and this is sorely needed.
Kent Beck has a kata for experimenting with test order, which helps in gaining understanding. But that’s a far cry from a well-developed theory like we have for refactorings. So what do you think? If we understood this phase better, could we come up with the test writing equivalent of transformations and refactorings?