I’ve given you 3 Reasons Why It’s Important to Refactor Tests. But what’s actually involved in refactoring tests?
Disclosure: The book links below are affiliate links. If you buy anything, I earn a commission, at no extra cost to you.
Improve your test writing “Flow.”
Sign up to get my test-oriented code snippets.
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.
This is 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
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
(I quickly create well-formed
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? Leave a comment below.