We want to test-drive converting multiple characters in the tail of the word.
c2/22/SoundexTest.cpp | |
| TEST_F(SoundexEncoding, ReplacesMultipleConsonantsWithDigits) { |
| ASSERT_THAT(soundex.encode("Acdl"), Eq("A234")); |
| } |
A simple solution would involve iterating through all but the first letter of the word, converting each. But our code isn’t quite structured in a way that easily supports that. Let’s restructure the code.
One thing at a time, however. When test-driving, you want to keep each step in the cycle distinct. When writing a test, don’t go off and refactor. Don’t refactor when trying to get a test to pass, either. Combining the two activities will waste your time when things go awry, which they will.
We comment out the test we just wrote, temporarily halting our “red” activity. (In Google Mock, prepending DISABLED_ to the test name tells Google Mock to skip executing it. See Disabling Tests, to read about the implications of disabling tests.)
c2/23/SoundexTest.cpp | |
* | TEST_F(SoundexEncoding, DISABLED_ReplacesMultipleConsonantsWithDigits) { |
| ASSERT_THAT(soundex.encode("Acdl"), Eq("A234")); |
| } |
We focus on refactoring activity and rework our solution a little. Rather than pass the entire word to encodedDigits, we pass it the tail of the word—all characters except the first. Passing only the tail should simplify the code we’ll need to iterate through the letters to be converted. It also allows us to use a couple string functions that help clarify what the code does: empty and front.
c2/23/Soundex.h | |
| std::string encode(const std::string& word) const { |
* | return zeroPad(head(word) + encodedDigits(tail(word))); |
| } |
| |
| private: |
| // ... |
* | std::string tail(const std::string& word) const { |
* | return word.substr(1); |
* | } |
| |
| std::string encodedDigits(const std::string& word) const { |
* | if (word.empty()) return ""; |
* | return encodedDigit(word.front()); |
| } |
We run our tests again to ensure our changes break no tests. That ends the refactoring activity.
We return to the start of the TDD cycle by reenabling ReplacesMultipleConsonantsWithDigits and watching it fail. We get our tests to pass by using a range-based for loop to iterate the tail of the word.
c2/24/Soundex.h | |
| std::string encodedDigits(const std::string& word) const { |
| if (word.empty()) return ""; |
| |
* | std::string encoding; |
* | for (auto letter: word) encoding += encodedDigit(letter); |
* | return encoding; |
| } |
Now that we’ve added a loop to encodedDigits, we think we don’t need the guard clause to return early if the word passed in is empty. As a refactoring step, we remove it.
c2/25/Soundex.h | |
| std::string encodedDigits(const std::string& word) const { |
| std::string encoding; |
| for (auto letter: word) encoding += encodedDigit(letter); |
| return encoding; |
| } |
We rerun our tests. Success! Deleting unnecessary code is extremely satisfying, but only when we can do so with confidence. Having tests to try these little bits of cleanup rocks.
3.144.232.189