Adding Unit Tests to an existing iOS project with Xcode 4
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).
59 Comments
Claudio
July 28, 2011Thanks 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?
Todd Huss
July 29, 2011If 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.
chirred
August 23, 2011Thanks! 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?
Todd Huss
August 23, 2011Make 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
Edward
November 30, 2011I 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:
Kyle
March 12, 2012What 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.
chirred
August 24, 2011I 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
Sam
September 11, 2011In your tests target try setting “Test After Build” to Yes.
Beat
September 1, 2011I 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’
Christopher Pickslay
September 1, 2011That’s great, Beat. Thanks for sharing the config!
PokerChang
September 6, 2011Great 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
Rémi
October 18, 2011I 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?
Christopher Pickslay
October 18, 2011It’s still the same. It may show up as “GCC_SYMBOLS_PRIVATE_EXTERN” if you have “Show settings names” set from the Editor menu.
Paulo
November 4, 2011This 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?
johan
January 19, 2012Paulo, 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
David
November 20, 2011Hi, 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!
Daniel Dyba
February 8, 2012When 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.
scandidavian
November 30, 2011In 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.
David
September 7, 2012This needs to be added to the instructions! Thanks.
MandyW
December 8, 2011Many 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!
Jacob
December 16, 2011Great 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.
Christopher Pickslay
December 16, 2011Cool, glad to hear it helped!
Jason Ryan
February 3, 2015Thank you so much, I want to cry now! So exciting.
jianhua
December 24, 2011Greate post, one question, what’s the purpose of step 6, set “Symbols Hidden by Default” build setting to NO?
Christopher Pickslay
January 19, 2012If 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.
Pioter
February 6, 2012Thanks a lot, this helps me.
Mickaël
February 21, 2012Thanks 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… 😉
Jasper Blues
March 2, 2012Great tutorial – it’s hard to find this information in the Apple documentation.
Jonas Andersson
March 28, 2012Got 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?
Christopher Pickslay
March 28, 2012It 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”.
Huy Le Duc
March 22, 2013Great tutorial. I’ve spent 8 working hours to find your answer.
amber
June 4, 2013Thank you so much! This is the answer really solved my problem.
Adjeiinfon
April 13, 2012Thank 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?
LP
April 20, 2012Go 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
Sam Kim
August 15, 2012I 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.
Stas
October 9, 2012Great 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
John Seitz
November 21, 2012So 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.
horseshoe7
December 29, 2012I 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.
Lee Probert
January 9, 2013Don’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.
Cocos2D Revised Template « Thought Repository
January 9, 2013[…] Next, onto adding Unit Testing. The next bit was inspired by this post: […]
Rick
January 31, 2013Great post! This really saved me. I don’t understand why these settings don’t get added by default.
Linked to this post on StackOverflow
April 14, 2013I 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)
rL
April 22, 2013Thanks so much. I’ve been struggling with this issue for a week now with my setup, and this did the trick!
Daniel Beard
May 11, 2013Quick and to the point, thanks!
Steffen
May 20, 2013Helped a lot thank you. Was only missing the Test Host setting.
Bhaskar
May 21, 2013Fantastic tutorial.. was very worried with the linker errors..
dr.alpha
June 13, 2013Perfect, thanks for the post. It’s working like a charm now.
HUy
July 25, 2013Thanks a lot. Your post saves me a lot of time
Recipe: a Podfile with different Pods per target | Thought Repository
August 13, 2013[…] Also assuming you checked the box ‘Include Unit Tests’ when you set up your Xcode project. Otherwise, see here […]
Chris
September 6, 2013Great post, it fixed my exact problem. I wonder why Xcode just doesn’t do this for you though during the unit test project setup? In my case I set up the unit test project after I setup the project under test first. IOW, I did not choose to add a unit test target during the initial project setup. Would that have made a difference?
Aqib Mumtaz
December 6, 2013The last step is to configure your unit tests to run when you trigger a test (⌘U). Click on your scheme name and select “Edit Scheme…”. Click on “Test” in the sidebar followed by the “+” in the bottom left corner. Select your testing target and click “OK”.
Kle Miller
April 7, 2014Hi All,
if you have done all the above and it still isn’t working check out this post (from the SO link provided in the article)
http://stackoverflow.com/a/12600218
Basically it says
Set Deployment Postprocessing (in the Deployment section of Build Settings) to NO for the Debug target.
Before I did this, the executable was being stripped, and the link would fail with
Undefined symbols for architecture i386:
“_OBJC_CLASS_$_SomeClassUnderTest”, referenced from:
objc-class-ref in SomeTest.o
No matter that Strip Linked Product and Strip Debug Symbols During Copy were set to NO, it made no difference – only changing the Deployment Postprocessing setting finally made sure that the symbols were not stripped.
Cheers
Kle
Charles Wang
September 16, 2014Thanks a lot for your help
Sebastian Mecklenburg
February 24, 2015For the life of me, I can’t get it to work. Xcode can’t find the executable file I set for the bundle loader, the $(BUILT_PRODUCTS_DIR)/MyExistingApp.app/MyExistingApp path. When I paste the path into the console it is there, so it is correct but the linker stubbornly tells me “File nor found”. What could be the reason for this?
Ye Tian
August 18, 2015Hahaha, thank you so much!
Xcode 7: linker command failed with exit code 1 (use -v to see invocation) again « news-Knowlage FeeD
October 29, 2015[…] already try this: http://twobitlabs.com/2011/06/adding-ocunit-to-an-existing-ios-project-with-xcode-4/ like suggested in this post: clang: error: linker command failed with exit code 1 (use -v to see […]
Ali
October 29, 2016Getting
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Xcode 8
Aisha
June 17, 2021Amazing post!!!!! Thanks you so much for sharing.
Link error while building a unit test target – Row Coding
July 17, 2023[…] the instructions here. It doesn’t require you to add any files to compile […]