“My current test does not test the final version of the feature yet. Should I change the test or add a new test?”
To answer this question, I want to talk about a concept called “Triangulation”.
Intro (TL;DR)
How can you determine that your production code does the right thing when it also hides the implementation and internal state, as it should?
- You observe the publically visible behavior for well-known examples; that is, you use indirect measurement AND
- You combine the results of multiple such tests to determine that the production code does the right thing.
In test-driven development, we call this technique “Triangulation”.
I’m David, a trainer and technical agile coach with over 12 years of experience. You are watching part 6 of an 8-part mini series about the phases “Red” and “Green” of the TDD-cycle “Red-Green-Refactor”.
Question / Problem
In my trainings, I often ask attendees to implement a “Hangman” game. They decide that the game should know the word to guess and to keep track of the user’s progress.
They start to write a test for the word to guess. It creates a hangman game and then asserts that the game knows the secret word.
@Test
public void knowsWordToGuess() {
Hangman hangman = new Hangman("hubkolbenverbrennungsmaschine");
assertThat(hangman.getWordToGuess()).isEqualTo("hubkolbenverbrennungsmaschine");
}
And I tell them: “Wait, you cannot do that! The game must keep the word to guess secret. Giving it back to the caller whenever they ask for it is not good design. It violates encapsulation and information hiding. And it can also lead to more design problems down the road, like feature envy or too closely coupled code.”
“OK, but how can we re-write this test so that it does not need a getter for the word to guess?”
Solution
The simple answer is: You can’t. You cannot write this test without the getter.
But you can make sure that the game knows the secret word by using multiple indirect measurements. By testing the visible behavior of the code through it’s public API using multiple tests.
At the beginning, the game renders a hint with underscores for every letter of the word to guess. So you triangulate this functionality: You write multiple tests for the underscores.
- Hint is a single underscore for the word “a”
- Hint is three underscores for the word “the”
- Hint is five underscores for the word “hello”
Then, after each move, the game shows the letters that were already guessed correctly. You can triangulate this part of the functionality again with a few tests:
- Shows the “h” when the word was “hello” and the user guessed “h”.
- Shows the two “l”s when the word was “hello” and the user guessed “l”.
- Shows the “h” and the two “l”s when the word was “hello” and the user guessed “h” and “l”.
When all those tests are green, you do not need a test for the word to guess. You can be sure that the game knows that secret word.
Conclusion
You can drive the implementation of a feature by using multiple, indirect tests. Then you know that your code implements this feature, even tough you cannot test the feature directly.
In TDD, we call this technique triangulation. It allows you to write well-encapsulated code in a test-driven way and to break your progress down into small steps.
CTA
How would you have tested that the game knows the secret word? How would you break your progress down into small steps in this case? Tell me in the comments or on Twitter, where I am @dtanzer.
And if you liked this video, please subscribe and share it with your friends and followers using the share buttons below - That would be awesome!
Read / watch all parts of this series here:
- TDD: Red-Green Part 8: Conclusion
- TDD: Red-Green Part 7: Specific / Generic
- TDD: Red-Green Part 6: Triangulation
- TDD: Red-Green Part 5: Into a Corner
- TDD: Red-Green Part 4: List of Goals
- TDD: Red-Green Part 3: Why Red
- TDD: Red-Green Part 2: Wrong Code
- Red-Green Part 1: Introduction
- Red, Green, ... Part 0: All Posts