I’ve given you 3 Reasons Why It’s Important to Refactor Tests. But what’s actually involved in refactoring tests?
The definitive book on this topic is xUnit Test Patterns: Refactoring Test Code by Gerard Meszaros. It’s a big book, full of patterns and smells. But there are 3 simple steps I take most often.
Here’s a five-minute screencast where I’m refactoring tests in my iOS TDD sample app using those 3 steps:
Step 1: Always start from green
Never refactor anything when tests are failing. This applies to refactoring production code, and to refactoring tests.
In any refactoring, start from green (tests passing) and end in green. Otherwise, you’re not refactoring.
Step 2: Extract what’s common into the test fixture
Some objects are common across tests, so extract them into the test fixture. I take one variable at a time and extract it to an instance variable.
Why not extract it to a property? Because I don’t want to change all the places it’s used across tests.
Why do I use ivars without underscore prefixes? Same reason. (I only do so in test code, though.)
Then I move the creation of the object into the setUp method.
If I’m extracting the System Under Test, I also set the ivar to nil in the tearDown method. This tests the full lifecycle of the SUT, down to its dealloc.
(I quickly create well-formed setUp and tearDown using templates. You can get them here.)
Step 3: Extract what’s not common into helper methods
What do you do with configurations that are common across some tests, but not all tests? Don’t create them in setUp. Instead, extract them to small helper methods.
In the screencast, you can see I extract a dummyRequestModel helper method. Choosing good method names makes tests more readable. A good test expresses what is important, and hides what is unimportant.
Extracting helper methods applies to all three phases of a test: Arrange, Act, Assert. But be careful with Assert helpers. You want test failures to report the line number of the location in the test, not the location in the helper. You can do this with preprocessor macros. But there’s a more flexible and reusable approach: define your own custom OCHamcrest matcher.
Take a moment and look over some of your unit tests. Can you find anything to extract to the test fixture? Is there anything you can extract to helper methods?
I did this exercise myself. My eyes were drawn to any tests with repeated concepts. They’re pretty easy to spot, since these tests are positioned close to each other. I looked for repeated objects and repeated code. I paid close attention to any Arrange section with more than 3 lines.
My tests are now easier to read than before. And it was fun!
I also did the Bowling Game TDD Kata recently. It reminded that anything that has comments ought to be extracted. This is true even if it’s used in only one test. So look for repeated test code, but also watch out for any comments.
Don’t forget to run tests before and after refactoring!
Did you discover anything to clean up in your tests? Do you have any questions about test refactoring? Click here to leave a comment.