When you build a new iPhone or iPad app from scratch, you can generate a new project with tests using Xcode’s project templates, and the test dependency is set up correctly.
However, if you add a unit test target to an existing iOS project, there are some manual steps required to set up the target correctly. Following these steps will get you up and running quickly:
- Project MyExistingApp -> + Add Target -> iOS -> Other -> Cocoa Unit Testing Bundle
- Name the new target something like “Unit Tests” or “MyAppTests”
- Select your new “Unit Tests” target and click the Build Phases tab, expand Target Dependencies and add MyExistingApp as as a Target Dependency
- Click the Build Settings tab and set the Bundle Loader setting to
$(BUILT_PRODUCTS_DIR)/MyExistingApp.app/MyExistingApp
- Set the Test Host build setting to
$(BUNDLE_LOADER)
- Go back to your app target (not the test target), set the Symbols Hidden by Default build setting to
NO
That should take care of it, and you can now start adding tests for your app. With this configuration, you continue to add application classes only to your app target, and add test classes only to your test target. Since the tests run in the app running in the simulator, the code under test can safely instantiate views and fonts, which would otherwise cause OCunit to crash when running without the Test Host setting.
Undefined symbols for architecture i386
If you add a unit test target without following these steps, you will likely run into linker errors when you try to run tests, like the following:
Undefined symbols for architecture i386:
"_OBJC_CLASS_$_SomeClassUnderTest", referenced from:
objc-class-ref in SomeTest.o
This is because your tests reference classes in your app which aren’t present in the test binary. The target dependency just causes the app to build before building the tests. Setting the Bundle Loader, Test Host, and Symbols Hidden By Default settings as described above allows the tests to link against your app, and should resolve these errors.
If you continue to see linking errors check this stack overflow post for other possible solutions (thanks Johan).
Thanks a lot for this post.
One question, in my case the MyExistingApp is executed on the simulator, can the configuration be changed in order to avoid the execution of the app?
If you skip step 5 you can run your tests outside of the simulator. Here at Two Bit Labs we typically don’t do that though since you can really only effectively test the model layer outside of the simulator. By running your tests in the simulator you’ll be able to load up controllers and views, click buttons, and do a whole bunch of interesting integration and functional testing that you can’t effectively do outside of the simulator.
Thanks! Unfortunately I’m still getting the i386 error, even after setting the Symbols Hidden by Default to no in the App target. Anything I’m not seeing maybe?
Make sure you have step 4 (setting the value of the Bundle Loader) correct. It’s an easy one to get wrong. For example if your app includes a space like “My App” then you’d need to set the value to:
$(BUILT_PRODUCTS_DIR)/My App.app/My App
I did the same thing, and still getting the same problem. Is there another way around ? I did all the steps all over again, and its still giving the:
Undefined symbols for architecture i386:
What should I put as the Bundle Loader path for a static library? I tried “$(BUILT_PRODUCTS_DIR)/commonios.a/commonios” and a few others, but none of them worked.
I have checked it, if I put in a different name I get different related errors, so I’m sure it’s correct
Still can’t seem to work out the problem, I’ll keep trying. Thanks though
In your tests target try setting “Test After Build” to Yes.
I tried this with an Mac Application project. The only thing I had to change there was to set the Bundle Loader to:
$(BUILT_PRODUCTS_DIR)/MyExistingApp.app/Contents/MacOs/MyExistingApp
instead of
$(BUILT_PRODUCTS_DIR)/MyExistingApp.app/MyExistingApp
Thus adding the subfolders ‘Contents/MacOs’
That’s great, Beat. Thanks for sharing the config!
Great post to get started on OCUnit. However, I ran into some strange bugs while testing UINavigationController and UITableView. Any help is appreciated!
http://stackoverflow.com/questions/7326961/unit-test-uinavigationcontroller-and-uitableview
I have build a new xcode project with unit test, but ran in the above described issue:
Undefined symbols for architecture i386:
“_OBJC_CLASS_$_SomeClassUnderTest”, referenced from:
objc-class-ref in SomeTest.o
I tried your settings but couldn’t find “Symbols Hidden by Default “. I’m using Xcode 4.2. Did the this settings name changes?
It’s still the same. It may show up as “GCC_SYMBOLS_PRIVATE_EXTERN” if you have “Show settings names” set from the Editor menu.
This is very helpful. However I am getting a link error at run time (not the i386 error at compile time)
The test bundle at /Users/me/dev/build/Debug-iphonesimulator/BackupAndRestoreTests.octest could not be loaded because a link error occurred. It is likely that dyld cannot locate a framework framework or library that the the test bundle was linked against
Have you any idea how I have configured things incorrectly?
Paulo, this might come a little late but for others pulling their hair out in frustration, this might give them a hint: http://stackoverflow.com/questions/6302362/xcode-4-fails-to-initiate-unit-tests-with-linker-error-after-i-created-a-new-s
Hi, I followed your instructions and was able to compile succesfully. However, when running the test target i get a runtime error:
warning: This configuration supports “Darwin64″ but is attempting to load
an executable of type i386 which is unlikely to work.
Attempting to continue.
`/usr/lib/dyld’ has changed; re-reading symbols.
warning: Inconsistent DBX_SYMBOL_SIZE
/Users/…/Tests.app/Tests: /Users/…Tests.app/Tests: cannot execute binary file.
This happens even if there havent been anything added to the Tests app that targets the Existing App. Another error I was getting was
ld: -bundle_loader can only be used with -bundle.
That went away when I changed Mach-O Type to bundle….
Thanks anyways for the post, im almost there!
When I set the Mach-O build setting on my Spec target, I discovered that the tests are not displayed on the iPhone as they had been doing previously. Rather, the application launches and then immediately closes. Leaving the Mach-O build setting to executable gives me the error you had:
ld: -bundle_loader can only be used with -bundle.
In Xcode 4.2, I needed to go to Product – Edit Scheme, click on Test and add the MyAppTests bundle to Tests. Once I did this, the Test menu item was enabled and I could run tests in the simulator and on a device.
This needs to be added to the instructions! Thanks.
Many thanks for your great post. Your solution fixed some of my errors however when I run the tests from the command line (I’m using XCode 4.2 & iOS5), I get:
Run unit tests for architecture ‘i386′ (GC OFF)
/Developer/Tools/RunPlatformUnitTests.include:419: note: Running tests for architecture ‘i386′ (GC OFF)
2011-12-08 11:39:27.666 otest[2017:7803] The test bundle at /Users/Mandy/Documents/TheElementsTwo/build/Debug-iphonesimulator/AnotherTryUnitTest.octest could not be loaded because a link error occurred. It is likely that dyld cannot locate a framework framework or library that the the test bundle was linked against, possibly because the framework or library had an incorrect install path at link time.
/Developer/Tools/RunPlatformUnitTests.include:448: error: Failed tests for architecture ‘i386′ (GC OFF)
/Developer/Tools/RunPlatformUnitTests.include:462: note: Completed tests for architectures ‘i386′
** BUILD FAILED **
The following build commands failed:
PhaseScriptExecution “Run Script” build/TheElements.build/Debug-iphonesimulator/AnotherTryUnitTest.build/Script-38382AC61490D6FE00DE43EA.sh
Many thanks for any help you can give!
Great post, exactly the information that I was looking for. The one thing that got me at first was setting the “Hide symbols by default”. I kept setting it for the testing target, once I realized it should be set for the existing app target, I was golden.
Thanks again.
Cool, glad to hear it helped!
Greate post, one question, what’s the purpose of step 6, set “Symbols Hidden by Default” build setting to NO?
If you don’t do that, you’ll get a bunch of “Undefined symbol” errors when you try to build your test target, because it can’t see the symbols from your app target. You can leave symbols hidden in your Release configuration.
Thanks a lot, this helps me.
Thanks for this. It really helped.
Just in case other people have the same bad eyes I had the 1st time, the “Symbols Hidden by Default” parameter is to set to “No” on the target you TEST, not the unit tests target. Lost 2 hours…
Great tutorial – it’s hard to find this information in the Apple documentation.
Got the error “ld: -bundle_loader can only be used with -bundle” after following your tips. Can’t find anything about this error. How do I solve it?
It sounds like your unit test target is misconfigured. In the “Build Settngs” tab of your test target, look for the “Mach-O Type” setting under “Linking”. It should be set to “Bundle”.
Great tutorial. I’ve spent 8 working hours to find your answer.
Thank you very much for this tutorial.
I followed the steps but it seemed anyhow that my test is no run.
I finished up by adding manual the my app (.m)files in to compile sources in order to get my test run.
Is there any other way to solve this problem?
Go back to your app target (not the test target), set the Symbols Hidden by Default build setting to YES. Compile it will fail.
then go back again to our app target (not the test target), set the Symbols Hidden by Default build setting to NO and compile again. I
I am getting the following error, but the file does exist on the path specified:
The executable for the test bundle at /Users/me/Library/Developer/Xcode/DerivedData/OldApp-cuzlyiywswfchbgyclwrcjklrtoa/Build/Products/Debug-iphonesimulator/OldAppTests.octest
Any ideas on why this might be happening?
Thanks.
Great tutorial, though I’m a bit stuck on adding logic tests to my app (app tests work fine!) can you please help me and answer my question on stackoverflow.com, here’s the link http://stackoverflow.com/questions/12781589/adding-logic-tests-to-the-project-in-xcode-4-5
So I deleted my Unit Test target that Xcode created for me since it was getting a dyld link error. Followed your direction exactly, and everything is working perfect. So, thank you for that.
But the previous unit test target, when there was a failure, I could click on the failure, and it would show me the source code line where the fail occurred. Now this new target doesn’t.
Any suggestions on returning the code line functionality.
Thank you again for a great tutorial.
I ran into an issue in that I import my key frameworks (that I need throughout the app, for example cocos2d.h) in my .pch file on my main target. I was getting ‘file not found’ errors when trying to build my unit tests that have the main target as a dependency. Ensuring these imports from the main target’s .pch were also in the test target’s .pch solved the problem.
Don’t forget you’ll probably want to delete the scheme for the testing and then edit the project scheme so you can include the test bundle to the Test configuration. Then you just need to hit cmd-U to run your tests without changing schemes.
Pingback: Cocos2D Revised Template « Thought Repository
Great post! This really saved me. I don’t understand why these settings don’t get added by default.
I just linked to this article, from this stack-overflow question:
http://stackoverflow.com/questions/15996347/storyboard-ui-testing-from-code/15998879#15998879
(one of my fav TwoBit articles)
Thanks so much. I’ve been struggling with this issue for a week now with my setup, and this did the trick!
Quick and to the point, thanks!
Helped a lot thank you. Was only missing the Test Host setting.
Fantastic tutorial.. was very worried with the linker errors..