You may have seen the 3 steps of “the TDD waltz”: Fail, Pass, Refactor. There are many ways to do it wrong! Two common mistakes are:
- Skipping the refactoring step
- Not skipping it, but refactoring only production code
So let me give you 3 reasons why it’s important to refactor tests.
[This post is part of the series TDD Sample App: The Complete Collection …So Far]
1. Tests Are Code, Too
Test code isn’t the forgotten cousin of production code. In fact, good tests are more valuable than production code. If you have a rich suite of tests, you can derive production code that satisfies them. But going the other way—deriving test code from production code—is much harder.
Like any code, tests can rot if neglected. Treat test code with the same respect you give to production code. If you refactor production code, why wouldn’t you refactor tests?
(You do refactor production code, don’t you? And I don’t mean “changing things” or “rewriting.” I mean real refactoring, by the book.)
Treat test code with the same respect you give to production code.
2. Tests Should Be Readable
As with any code, readability is vital. Why? Because code isn’t just for computers; it’s for people. Think of the next person who will have to read your tests. (…That person may be you! In which case, you’re doing your future self a favor.)
A big part of readability is hiding details in methods with well-chosen names. Good names express what something does, or why. The details of how are there in the method, but only if you need to see them.
Refactor tests to have good names, good variables, and well-named helper methods. The better a test expresses itself, the less trouble you’ll have maintaining it.
3. Tests Should Be Easy to Update
Production code keeps changing. When that happens in a way that requires tests to also change, which would you rather do?
- Update 20 different tests.
- Update 1 helper method used by 20 tests.
Some people say that test code shouldn’t be DRY (Don’t Repeat Yourself). There is a balance to hit: test code shouldn’t have layers upon layers of abstractions. But that shouldn’t be an issue, because good test code is simple to begin with:
- No conditionals
- No loops (except rarely, for repeating calls)
So I’ve found that one layer (occasionally two) of small helper methods helps keep tests easy to maintain.
Refactor Tests: Because It’s All Refactoring
Renaming, moving things around, extracting methods… This is all basic refactoring that applies to any code, including test code. So don’t be afraid to refactor tests.
Next time, I’ll show you 3 steps to apply when refactoring tests.
Are your tests “set and forget”? When was the last time you refactored any tests? Share your thoughts, experiences, and questions in the comments below.
[This post is part of the series TDD Sample App: The Complete Collection …So Far]
I always find myself with a lot of dublicate testing code. Mainly due to the fact, that tests should be independant from one another. So for example if my app contains two UITableViewController-Subclasses, I will have two TestClasses for that. Both will contain Testcases like:
TestThatTableViewHasDataSource
TestThatTableViewHasDelegate
TestThatOneFoodEntryReturnsOneRow
TestThatTwoFoodEntriesReturnTwoRows
TestThatTwoFoodEntrysWithSameTimeReturnOneSection
TestThatTwoFoodEntrysWithDifferentTimeReturnTwoSections
TestThatTableViewCellReturnsNameUnitAndCaloriesOfFoodItem
TestThatTableViewCellInLastSectionHasAddSymbol
TestThatTableViewCanBeSetIntoEditingMode
TestThatCellsCanBeDeleted
TestThatCellsCanBeMovedIntoAnotherSection
TestThatAddSectionEntryCannotBeMoved
TestThatAddSectionEntryCannotBeDeleted
TestThatSelectingAnEntryInEditModePushesEditEntry
TestThatSelectingAnEntryInEditModeFalseShowsEntry
…
Is there some refactoring possible I am missing out?
Timm, I guess it depends: How much duplication is there, really? Not just in the test cases (shown by the names you list) but in the test code itself.
If you have truly duplicated tests, then you could pull them to a base class. But should you? A common refactoring rule-of-thumb is “3 strikes and you refactor.” So for two sets of similar tests, I’d shrug. For three, I’d extract them.
Then I’d look for parallel refactoring opportunities in the production code.