Code Kata
Do you want to become really good at Test-Driven Development (TDD)? Learning transformations is one of the best ways to get there. Transformations provide a methodical approach to passing your tests while realizing a high-quality design.
Transformations are the opposite of refactoring. Refactoring changes (improves) the structure of the code without significantly changing its behavior, while transformations change the code to generalize behavior without significantly changing its structure.
The common pattern for TDD is the Red - Green - Refactor cycle. In the Red phase, we first write just enough of a unit test to demonstrate a failure. Then in the Green phase, we write just enough production code to make the test pass. But we should think of this phase not just as the Green phase, but as the Transform-to-Green phase. That's because we use transformations to get the tests to pass.
These are the 12 transformations that Bob Martin developed and prioritized to make TDD a little simpler and straighforward. A clean architecture usually results when applying them in this order.
-
Null
When first creating a new function, start by doing nothing more than return null. -
Null to Constant
Transforming null to a constant should be just enough to make your next test pass. This could be any type of constant, such as a numeric, string, or an object — whichever value will cause your test to pass. -
Constant to Variable
Changing a constant to a variable (or an argument) by itself does not alter behavior, but it is part of a sequence of transformations that enables other transformations. Simply create a variable in place of a constant and assign its value. -
Add Computation
This transformation is used to add computations or initialize variables, but can never change the state of an existing variable. This may include adding predicates or any mathematical operation. -
Split Flow
Here we add an if statement to split the flow of control into two (and only two) paths of execution. This allows two or more values to pass the test — which was enabled by the Constant to Variable transformation. Be sure to use the most general predicate that passes the test, i.e. use < or > as opposed to ==. -
Variable to Array
After making your tests pass for one thing, you will eventually need to make them pass for more than one of those things. This is where you want to transform your variable to an array to accommodate multiple occurrences of that one thing. -
Array to Container
This is used to generalize an array to something more generic such as a list, queue, or stack. -
If to While
This is used for a flow that has already been split and must now be repeated. And since a for loop is special case of a while loop, you can usually transform directly to a for statement. This is often used following a Variable to Array transformation. -
Recurse
To perform an operation over and over again, simply put it into a function and make the function call itself. -
Iterate
This transformation is used when you want to repeat an operation, but do not want to use recursion. -
Assign
Use this to change the state of a previously existing variable, but not to initialize a variable's initial state. -
Add Case
This transformation is used when we have a split flow that we want to split further. This occurs when we add an else to an existing if statement or a case to an existing switch statement.
Following are the 3 laws that define the discipline of TDD. By following these laws, along with the transformations above, you will be surprised how quickly and easily you realize a good solution to this problem.
- Write no production code except to pass a failing test.
- Write only enough of a test to demonstrate a failure.
- Write only enough production code to pass the test.