How to Rearrange Xcode Projects to Increase Testing

Shares

So you’ve tried to increase iOS unit testing in your organization.

Maybe you tried to lead by example, hoping others on your team would follow suit. Or maybe you tried to increase your own testing.

Either way, your efforts fell flat. Your new emphasis on testing was barely noticed, and eventually forgotten. Leading by example is key to introducing a test-driven culture to your organization. But it’s no good if no one sees your example.

If only there were a way to bring your tests in from the shadows, placing them front & center.

There is. It’s no silver bullet, but it’ll help. And it’s easier than you may think.

[This post is part of the series TDD Sample App: The Complete Collection …So Far]

The problem

Your Xcode project navigator probably looks something like this:

Test code group visible below collapsed production code

(If you’re curious about that XcodeWarnings.xcconfig, see Xcode Warnings: Can You Turn Them Up to Eleven?.)

When you create a new project, Xcode sets up a test target. Apple hooks it all up for you, with a group/folder that looks like a good home for test code. In the case of my MarvelBrowser project, you can see a group MarvelBrowserTests, right under the group for production code.

There’s a problem: we never work like this. We’re writing production code, so guess what? We’ll always expand the production code group to show its contents:

Test code group hidden below expanded production code

MarvelBrowserTests is getting pushed down by the expanded production code group. And I haven’t even added any code! Start adding a few files, and you won’t even see MarvelBrowserTests anymore.

Out of sight, out of mind. Someone opening your project may not even be aware that any tests exist.

The solution

That unhelpful arrangement for test code is similar to what I described in Is Apple Stuffing Your Code with Pointless Cruft?: Apple has decided to make things easier with a template. Unfortunately, the template leads us in the wrong direction.

My solution is simple: I eliminate that folder.

In the MarvelBrowser project, I made the following two commits:

  1. In MarvelBrowserTests under “Supporting Files”, I renamed Info.plist to Info-Tests.plist. In the test project, I fixed up the “Info.plist File” build setting. Build and run tests to confirm. (Commit bd7ac52, Rename test target Info.plist to Info-Tests.plist.)
  2. I moved everything in MarvelBrowserTests over to the MarvelBrowser folder. Again, this required fixing up the test target’s “Info.plist File” setting, this time to change the path. Build and run tests to confirm. (Commit 2cd3d8b, Move files from MarvelBrowserTests to MarvelBrowser.)

Now I can safely delete the MarvelBrowserTests group and folder. Going forward, production code and test code will be intermingled.

You do need to take a little more care when adding a new file, because you have to pay attention to specify the correct target. That’s a small price to pay for what you get.

4 benefits (and 1 potential drawback)

Here’s part of a different app I made:

Test code intermingled with production code

What are the benefits of arranging an Xcode project in this way?

1. Easier to find corresponding test code

Can you find the tests for ANJStarRatingImageView? …Easy, right?

In both Xcode and AppCode, there’s a keyboard shortcut to jump between a header and its implementation, between Foo.h and Foo.m. But there’s no shortcut to jump to a corresponding test file. So, let’s just keep them as neighbors in the project navigator.

2. Invites others to look at test code

To someone looking at that section of the project for the first time, the test files in the list are an invitation: “Look at me!”

Someone who wants to understand the semantics of the class can read the tests to see what the class does. (When writing tests, I keep tests-as-documentation as a goal in the back of my head.)

3. Provides examples of how to test

Developers new to testing often don’t know where to start. It’s natural to look at existing tests as examples. “How did you write a test for that?”

As you write tests, you also write test support code. These can include fake objects for your particular domain. They can also include testing idioms, like a way to write testable multi-threaded code. Test support code helps others get over the hump of “I want to test this, but it’ll take too long to figure out how.”

4. Easier to see if there is test code

It’s pretty obvious to anyone looking at this project that there are tests for ANJStarRatingImageView and ANJCommentCell. A natural question to ask is, “Where are the tests for ANJRatingCell?”

ANJRatingCell is either missing tests, or it’s thoroughly covered by other tests and doesn’t need tests of its own. There’s a big difference between the two, so which is it? Code coverage can answer that question, showing you where the holes lie.

5. Potential drawback: When not to intermingle

There is one case where I don’t intermingle production code with its test code. That’s when someone else will take my source files but use their own project file. In that case, it’s painful to have to walk through the source files and say, “This one goes in the app target, this one goes in the test target.” It’s easier to assign entire folders instead.

Unfortunately, this applies to anything distributed via CocoaPods, or any system that automatically assembles source files into a subproject. Though perhaps you can find a way to specify, “All *Tests.m files are test code, along with everything in this TestSupport folder. Everything else is production code.” Then you’d have the best of both worlds.

Tips for rearranging

Since groups and files in the project navigator doesn’t have to match the file system arrangement, you could simply drag your test files to new positions in the project navigator, without moving them in the file system. But there’s a more thorough way that is also quicker.

I think it’s important to change move them in the file system as well. Why? Because not everyone opens the project to look at your sources. Many times, people will go directly to the repository through its web interface. (By the way, this is one reason to prefer spaces over tabs for indenting code.)

So I find it helpful to move test code in the file system first. Assuming your project arrangement isn’t unusual, the quickest way to get things ordered in the Xcode project navigator is to remove everything, then add it back all it once. (This assumes your test file names share the same prefixes as their production counterparts.)

Again, here are the steps to take. Before you start, make sure your existing tests run cleanly. Note the total number of tests.

  1. In your test folder under “Supporting Files”, rename Info.plist to Info-Tests.plist. In the test project, fix up the “Info.plist File” build setting. Build and run tests to confirm.
  2. Move everything in your tests folder over to your main folder. Fix up the test target’s “Info.plist File” setting, this time to change the path. Build and run tests to confirm.

Finally, confirm that you still have the same number of tests running. Then pat yourself on the back: you’ve brought your tests out of the shadows!

By giving test code equal status with production code, everyone who works on your project will start to think more about testing.

Give it a try! How does it change the feeling of your Xcode project? You can share a comment by clicking here.

[This post is part of the series TDD Sample App: The Complete Collection …So Far]

About the Author Jon Reid

Jon is a coach and consultant on iOS Clean Code (Test Driven Development, unit testing, refactoring, design). He's been practicing TDD since 2001. You can learn more about his background, or see what services he can bring to your organization.

follow me on:
8 comments

Comments are closed