3 Easy Steps to Refactoring Tests for Greater Clarity

Click to play

February 16, 2016


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.

Code snippet sample

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.

At the top is a five-minute screencast where I’m refactoring tests in my iOS TDD sample app using those 3 steps. My example is in Objective-C, but the principles apply to Swift (and beyond).

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 a property. (In Objective-C, I extract to instance variables instead of properties.)

Then I move the creation of the object into the setUp method.

These days, I also set it to nil in tearDown. This is especially important for the System Under Test (SUT) because it tests the full lifecycle of the SUT.

But since making this video back in 2016, I no longer ask myself which properties to tear down. If you set it up, tear it down.

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

A good test expresses what is important, and hides what is unimportant.

Click to Tweet

Extracting helper methods applies to all three phases of a test: Arrange, Act, Assert. But be careful with Assert helpers. See how to make custom assertions in Swift.

(Objective-C requires even more care. 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. Now a coach with Industrial Logic!

  • 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!