February 16, 2016

3 Easy Steps to Refactoring Tests for Greater Clarity

Click to play


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.

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 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.

Practical exercise

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.

Can OCMockito's New Error Reporting Save You Time?

Jon Reid

About the author

Programming was fun when I was a kid. But working in Silicon Valley, I saw poor code lead to fear, with real human costs. Looking for ways to make my life better, I learned about Extreme Programming, including unit testing, test-driven development (TDD), and refactoring. Programming became fun again! I've now been doing TDD in Apple environments for 20 years. I'm committed to software crafting as a discipline, hoping we can all reach greater effectiveness and joy.

  • In his book, Jay Fields argues that extracting to a test fixture (i.e. making your tests DRY) is something you would rather want to avoid because it would compromise readability. He proposed to extract building the objects to a Test Data Builder (http://natpryce.com/articles/000714.html) instead (keeping the building of objects in the specific test method). How do you feel about that?

    • Let’s separate DRY from using a common test fixture. I haven’t read Jay’s book, but I can guess why he said that: you can end up with bloated setUp, if it contains stuff that isn’t used by most tests. But then some people take that to an extreme and write all their tests long-hand, repeating everything so that each test is fully self-contained.

      …But Test Data Builders are a great idea. Why? To hide what is unimportant, and express what is important. Seems pretty DRY to me.

      Just today, I had 6 tests fail because of an unrelated change. Thankfully, those tests used a helper method. I updated the helper, and they all passed again.

  • {"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}

    Never miss a good story!

    Want to make sure you get notified when I release my next article or video? Then sign up here to subscribe to my newsletter. Plus, you’ll get access to the test-oriented code snippets I use every day!