Unit Testing with Cocos2d and Xcode 4
Adding unit testing to my Cocos2d project took longer than I expected. In hindsight it was because I didn’t really understand how targets worked in Xcode. After stumbling around for a while, I figured out how to get this working. Here’s the steps I used. If you know a better/faster way, please leave a comment below.
If you already know Xcode well, this post probably isn’t for you.
Adding Unit Testing to an Existing XCode Project
Xcode offers a way to create tests with a new project, but the Cocos2d template does not. The first step is to create the test target.
1) Click on your project name in the left Project Navigator
2) Click Add Target on the bottom of your screen
3) Select Cocoa Touch Unit Testing Bundle. Click Next
4) Name your test whatever you’d like (ex: UnitTests). Click Finish
Now you’ve got tests added to your project. You can select your test name in the Scheme drop down menu. In the menu bar, click Product -> Test. You should receive the following error:
error: testExample (UnitTests) failed: Unit tests are not implemented yet in UnitTests
Success! This is what’s supposed to happen, because the only test you currently have is:
- (void)testExample { STFail(@"Unit tests are not implemented yet in UnitTests"); }
Testing Our Own Code
Now we want to actually test our code. Add the following to the top of your UnitTest.m file (or whatever you called your test file):
#import "cocos2d.h"
Add this method somewhere below:
- (void)testNode { CCNode *node = [[CCNode alloc] init]; STAssertNotNil(node, @"Node shouldn't be nil"); }
Disclamer: It doesn’t really make sense to test this. But since we have an empty project, we’re just trying to get the error to appear as an example. Using CCNode will trigger this error. In your project, you’d want to test things like calculations, transformations, etc..
Run Product -> Test to see the build failure:
Undefined symbols for architecture i386: “_OBJC_CLASS_$_CCNode”, referenced from: objc-class-ref in UnitTests.o
This is a link error, and is happening because our UnitTest target doesn’t know anything about our project. Usually when you create a new Xcode project, you create UnitTests from the beginning. When you add files to your project, you can select which targets the files belong to. Because we’re adding our testing after the fact, we have to add these manually.
Adding Compile Sources and Libraries
1) Click on your project name in the left Project Navigator
2) Click on your project target. This is not your testing target. It’s probably the one above it. Then click Build Phases
3) Expand the Compile Sources dropdown.
4) Select everything in the list
5) Right click. Click Reveal in Project Navigator
6) With the items in the Project Navigator still selected, click your testing target in the left Project Navigator
7) Click on Build Phases.
8) Drag and drop the selected files in the Project Navigator to the testing target’s compile sources
We’re almost done. Now you just have to add the library frameworks.
In your testing target’s build phases expand the Link Binary With Libraries dropdown.
You can manually add each framework required here (use your main project target as a reference for what should be here).
A faster way I found, was:
1) Expand the Frameworks folder in your Project Navigator
2) Select all frameworks in the folder (using Shift-Click or Command-Click)
3) Drag these frameworks to your test target’s Link Binary With Libraries dropdown
One more step. If you try to run your tests now, you’ll get these errors:
“_inflateInit2_”, referenced from: inflateMemory in ZipUtils.o
“_inflate”, referenced from: inflateMemory in ZipUtils.o
“_inflateEnd”, referenced from: inflateMemory in ZipUtils.o
“_gzopen”, referenced from: _ccInflateGZipFile in ZipUtils.o
“_gzread”, referenced from: _ccInflateGZipFile in ZipUtils.o
“_gzclose”, referenced from: _ccInflateGZipFile in ZipUtils.o
“_uncompress”, referenced from: _ccInflateCCZFile in ZipUtils.o
We need to add libz.dylib to the frameworks list under the Link Binary With Libraries dropdown section.
1) Click + under Link Binary With Libraries
2) Add libz.dylib
I’m not sure why this isn’t listed with the original project target libraries, but probably has to do with more Xcode underpinnings I don’t fully understand.
Whew. Go to Product -> Test to run your tests. It should compile successfully (but still fail because of testExample). You can remove – (void)testExample if you want to see the tests pass.
Create Test Shortcut (Optional)
This part isn’t necessary, but is very nice. We can simplify running our tests by adding them to our project scheme.
1) Select the Scheme dropdown menu, then click Edit Schemes
2) Select your project name in the scheme dropdown on the top of the dialog. This is not your tests you created
3) Select Test in the left hand view
4) Click the + icon right above Manage Schemes…*
5) Select your test. Click Add. Click OK.
Now you can go to Product -> Test without switching schemes first. Even better we can press Command+U to test.
Conclusion
Hopefully if you’ve run into this problem, these steps will solve it for you. This probably already makes sense to experienced Xcode users, but as someone new to the platform, it took me longer than I’d like. Hopefully this saves you some time.
This entry was published on June 5, 2011 in iOS
Thanks really useful tutorial.
I also found that when using the cocos2d with box2d template it was also necessary to copy the project target “Use Header Search Paths” entry to the “Header Search Paths” in the Build Settings of the Testing Target or Box2d could not find its header files.
This might be one of the best xcode tutorials ever. You were clear, concise, and didn’t go all pedantic. A great example would be:
“Click the + icon right above Manage Schemes…*”
Thanks.
Back when code signing was a pain all the tutorials were the exact opposite of this one.
Thanks again
You get the libz.dylib errors because you’re missing the “-lz” flag in Other Linker Flags in Build Settings. If you take a look at your original target, it has this flag set. Add the flag to avoid linking the Library manually.