In days gone by, I use to conduct in-person TDD instruction, imparting knowledge to hundreds of folks throughout my tenure. It was during this time, I fashioned the TDD Gears model as a tool to facilitate learners in comprehending the essence of the TDD domain.
TDD is a complex and challenging discipline filled with many practices and principles. It can be hard to understand the reasoning behind some practices such as the rule of 3 or triangulation while solving seemingly simple katas.
At its core, TDD is a thinking tool. A tool to solve problems at the micro scale and to apply key software development principles. The TDD gears model was developed to help explain how to utilize TDD as a thinking tool.
Often as developers, we are used to solving a problem at the monolithic scale, attempting to hold all the moving parts in our head while we bash away at the keyboard. By applying TDD we are forced to start solving problems on a micro scale. That is we need to tackle one small portion of the problem by conducting experiments to build and validate our mental models.
It seems sensible to believe because of the micro scale problem solving TDD was first created. In addition to helping slice up the problem, it also provides a framework for applying key development principles like SOLID, Stages of Naming and Single Level of Abstraction.
This application of key Software Development principles can be achieved by purposefully reading and reflecting upon our code as we pass through each iteration of the TDD cycle. Thus the Red-Green-Refactor cycle becomes the Red-Green-Reflect-Refactor cycle highlighting the importance of applying software development principles iteratively as we evolve the solution.
The Gears
In the Gears model, each gear represents both a degree of adherence to the practices and scale of thought. The principles should always be strictly followed in all gears. This is because the principles are used to drive Professionalism by shaping the maintainability and quality of the solution. Professionalism must always be present regardless of the problem domain.
In low gear we are trying to build context. We move slow and deliberate trying to understand the problem at hand strictly following our practices and applying core green bar patterns like Triangulation and One-To-Many, while we avoid the Obvious Implementation trap.
As we shift into medium gear, we focus on building elegance through good design. Having established an understanding of the problem, we look to build maintainable code. We now have access to all the green bar patterns and can start to short circuit some practices like the Rule of 3. The risk here is that we get stuck and need to make use of reverse gear.
High gear is used to either follow existing patterns or solve a large test such as an integration or BDD style test. Because the goal is much larger the scale of thought required to solve the test needs to be larger. Being good students of TDD we will have built up to solving a large test by shifting through low and medium gear. It is because of this stepped approach we can take on more risk while still being quite relaxed about the application of various TDD practices.
Reverse gear is used to get unstuck. In any gears, it is possible to find yourself stuck, reverse offers up a solution on how to make progress when you have been struggling with a red test or tricky refactor.
A good rule of thumb for when to use reverse is red/red/reverse. That is if after two attempts to make the failing test pass you cannot, reverse back to green test run and try again.
Figure 1: TDD Gears Model
- Low
- Unsure of Domain or algorithm being implemented
- Evolve slowly to build context - understand the problem
- Strict following of all core practices
- No advanced practices used
- Low risk of reversing
- Medium
- Some familiarity with domain or algorithm being implemented
- Evolved to build elegance - create good design
- Short circuit some TDD practices
- Mixes advanced and core practices
- Moderate risk of reversing
- High
- Familiarity with domain or algorithm being implemented
- Evolve to build substance - follow existing patterns or solve a larger test
- Heavy short-circuiting of TDD practices
- Mixes advanced and core practices
- Moderate / High risk of reversing
- Reverse
- Like getting stuck in the mud it is best to reverse and try a different angle.
- A good rule of thumb for when to use reverse is red/red/reverse.
- If after two attempts to solve the test it is still red, reverse.
- When to use:
- Struggling to complete a refactoring
- Struggling to find next test
- Feels like the solution is stuck and cannot move forward
- How to use:
- Apply the Back Out pattern
- Restart in Low gear
- Find a new point of attack
- Use your test to take small steps to build a “virtual stack trace” validating your understanding of the code at each step.
- Use a Learning Test to validate a portion of the problem if struggling to understand the inner workings of a private method.
TDD Practices and Principles
A listing of all practices and principles that one should consider when applying the TDD cycle. The gears model is about apply the cycle as you write code.
Core TDD Practices
- Rule of 3
- Allow duplication to appear 3 times before you abstract it.
- Green Bar Strategies
- Fake It
- Return a constant
- Triangulation
- Remove Fake It
- Implement for 3 Fake It test before moving toward a generic solution after the 3rd.
- One to Many
- Single to collection
- Fake It
- TDD Cycle
- Strict
- Always ensure a test fails for the correct reason before continuing. If it passes straight away make it fail first.
- Strict
- Stages of Naming
- Move from meaningless to specific to meaningful names.
- Equivalence Partitions and Boundaries
- Test Factory Methods
- Abstract common test setup per file
- Test Doubles
- Received Asserting
- Assert a method was called with the correct data.
- Received Asserting
Advanced TDD Practices
- Green Bar Strategies
- Obvious Implementation
- If the implementation is obvious, implement it
- Obvious Implementation
- Test Factory Methods
- Avoid Default arguments in method
- Avoid Optional Parameters in method
- Test Data Builders
- Abstract common test setup
- Uses Fluent syntax
- Test Doubles
- Context Asserting
- Assert the correct result was achieved through the correct use of various test doubles.
- Context Asserting
TDD Principles