Abstractly, the cycle we repeated in the previous section’s development process was as follows:
- We wrote a (unit) test for the next piece of functionality we wanted to implement. The test failed.
- We implemented the functionality. The test passed.
- We refactored our production and test code.
This TDD process is also called the red-green-refactor cycle. Figure 8.1 shows a popular way to represent the TDD cycle.
Figure 8.1 TDD, also known as the red-green-refactor cycle
TDD practitioners say this approach can be very advantageous for the development process. Here are some of the advantages:
- Looking at the requirements first —In the TDD cycle, the tests we write to support development are basically executable requirements. Whenever we write one of them, we reflect on what the program should and should not do.
This approach makes us write code for the specific problem we are supposed to solve, preventing us from writing unnecessary code. And exploring the requirement systematically forces us to think deeply about it. Developers often go back to the requirements engineer and ask questions about cases that are not explicit in the requirement.
- Full control over the pace of writing production code —If we are confident about the problem, we can take a big step and create a test that involves more complicated cases. However, if we are still unsure how to tackle the problem, we can break it into smaller parts and create tests for these simpler pieces first.
- Quick feedback —Developers who do not work in TDD cycles produce large chunks of production code before getting any feedback. In a TDD cycle, developers are forced to take one step at a time. We write one test, make it pass, and reflect on it. These many moments of reflection make it easier to identify new problems as they arise, because we have only written a small amount of code since the last time everything was under control.
- Testable code —Creating the tests first makes us think from the beginning about a way to (easily) test the production code before implementing it. In the traditional flow, developers often think about testing only in the later stages of developing a feature. At that point, it may be expensive to change how the code works to facilitate testing.
- Feedback about design —The test code is often the first client of the class or component we are developing. A test method instantiates the class under test, invokes a method passing all its required parameters, and asserts that the method produces the expected results. If this is hard to do, perhaps there is a better way to design the class. When doing TDD, these problems arise earlier in the development of the feature. And the earlier we observe such issues, the cheaper it is to fix them.
NOTE TDD shows its advantages best in more complicated problems. I suggest watching James Shore’s YouTube playlist on TDD (2014), where he TDDs an entire software system. I also recommend Freeman and Pryce’s Growing Object-Oriented Systems Guided by Tests (2009). They also TDD an entire system, and they discuss in depth how they use tests to guide their design decisions.
Leave a Reply