65

Recently I started Unit testing my application. This project (in Xcode4) was created without a unit test bundle so I had to set it up. I have followed the steps from here: http://cocoawithlove.com/2009/12/sample-mac-application-with-complete.html And It was working well for simple classes but now I am trying to test a class that depends on another and that on another, etc.

First I got a linker error so I added *.m files to the test case target but now I get a warning for every class I am trying to test:

Class Foo is implemented in both MyApp and MyAppTestCase. One of the two will be used. Which one is undefined.

I wonder why is that? How can I solve this? Maybe I missed something when setting the unit test target?

Edit - The Solution

  • Set "Bundle Loader" correctly to $(BUILT_PRODUCTS_DIR)/AppName.app/AppName

  • Set "Symbols Hidden by Default" to NO (in Build Settings of the target application). This is where the linker errors come from because it is YES by default!. I've been struggling with this for so long!.

Source: Linking error for unit testing with XCode 4?

Community
  • 1
  • 1
nacho4d
  • 43,720
  • 45
  • 157
  • 240
  • 1
    If you are using Cocoapods and your tests are running in a host application Richard's solution can solve the issue. – Nick Weaver Mar 09 '16 at 12:08
  • In my case I just restarted MacOS and worked :S – diegomercado Feb 15 '17 at 18:23
  • 2
    Setting the Bundle Loader as you have it gives me a linker error _“ld: file not found: …/DerivedData/AppName-…/Build/Products/Debug-iphoneos/AppName.app/AppName”_.  It seems that at the point in linking the `…/AppName.app/` dir exists but the `/AppName.app/AppName`executable does not yet exist, so it can't use it for that arg.   |   _Note: Downvoting because your solution doesn't work for me.  You should really post your solution as an answer, not as part of your question; as part of your question I have to downvote the whole thing._ – Slipp D. Thompson May 18 '17 at 18:53

6 Answers6

45

Class Foo is implemented in both MyApp and MyAppTestCase. One of the two will be used. Which one is undefined.

I wonder why is that?

because both images (the app and the unit test bundle) define the implementation of the class. the class is dynamically loaded into the objc runtime. the objc runtime uses a flat namespace. how this works:

  • the binary is loaded, starting with its dependencies
  • as each binary is loaded, the objc classes register with the objc runtime
  • if a class with a specific name is loaded twice, the behaviour is undefined. one implementation of a class (with identical names) can be loaded into the objc runtime.

the typical problem here is that you will be returned one implementation - your app will likely crash when the type conflicts (when the class does not come from the same source file).

you typically avoid this by either renaming a class, or export the class in one image. renaming the class obviously does not apply to your case. you have one file Foo.m which is being compiled, exported, and loaded by two images when it should be in one.

this should be interpreted by you as a duplicate symbol linker error. even though the implementation is the same source file (and the implementation is the same) - this a problem that you must fix.

How can I solve this?

if Foo.m is a class of the app, you have to remove (do not compile and link) Foo.m from the unit test. if it's part of the unit test, then do not compile and link it into the unit test target.

then, follow the instructions in the post for linking/loading your unit test to the app. it's in this general area of the post: where "WhereIsMyMac" is the name of the application you're unit testing. This will let the testing target link against the application (so you don't get linker errors when compiling). the important part is that your test files are compiled in the unit test target (only), and your app's classes are compiled and linked into the app. you can't just add them - they link and load dynamically.

Maybe I missed something when setting the unit test target?

From the article you linked:

Note: The testing target is a separate target. This means that you need to be careful of target membership. All application source files should be added to the application target only. Test code files should be added to the testing target only.

the part that you got wrong is probably the link and load phases of the unit test bundle.

justin
  • 104,054
  • 14
  • 179
  • 226
  • i have a similar problem i've asked about elsewhere on SO. i've got a program that embeds webkit using a private framework (installed with @executable_path). this worked fine on Snow Leopard. with Lion, there is a component underneath the program (DataDetectors framework) that loads the system WebKit framework. this creates the same collisions. i can't rename all of WebKit's classes, and renaming the library won't help. i'm wondering whether a static library would solve things, but it doesn't sound like that is the case. – shaheen Jul 27 '11 at 23:41
  • @shaheen sounds like you understand the problem. a static lib will not help with objc. i don't know an easy solution for you. – justin Jul 28 '11 at 05:33
  • I am unable to access my app classes in xctest target. Getting `"_OBJ_CLASS_$_MYClassName", reference from: objc-class-ref in TestsCase.m ld:Symbol(s) not found for acrtichecture i386. ` When I add .m file in test case build phase this error is removed but while running on test cases I am getting following warning - `class is implemented in both. one of the two will be used. which is undefince xcode7 ios` – user1101733 Sep 02 '16 at 15:35
  • I just got the answer. Actually I am trying to add test case target into an existing project so in this case `Enable Testability' option was set to **NO** for debug mode as well. It solved my problem :-) Reference - [link](http://stackoverflow.com/questions/30787674/module-was-not-compiled-for-testing-when-using-testable/30792188) – user1101733 Sep 02 '16 at 19:53
18

If you are using Cocoapods, your podfile only needs the dependencies in the section for the main target, not the test targets. If you do add duplicate dependencies for the test targets, you'll get the OP's error message.

target 'MyProject' do
pod 'Parse'

end

target 'MyProjectTests' do

end

target 'MyProjectUITests' do

end
Richard
  • 1,249
  • 16
  • 25
  • After I added these dependencies, I removed from test and update pod but still I have problem. how to delete them? – Bernard Mar 08 '16 at 23:28
  • Delete the entire pod / lockfile / workspace and do another pod install? – Richard Mar 09 '16 at 01:03
  • Getting a little late to this... I'm in this exact scenario, trying to add Acceptance Tests with KIF. Run into the issue asked here and tried your solution; but now I'm getting `library not found for -lPods`. Any idea? Thansk @Richard – Andres C Mar 23 '16 at 17:54
  • Never mind @Richard. Sorry for bothering. The issue was I was linking against the old default lib "libPods", instead of only linking to the one for the target "libPods-target". – Andres C Mar 23 '16 at 18:05
  • 1
    For future users: Instead of `pod install` run `pod update` that will remove the unwanted files as well. – jeet.chanchawat Oct 18 '16 at 06:49
14

For me, all I needed to do was uncheck the checkbox that makes the Foo class a member of the unit test target. It should not be a member of both targets, and should look like this:

Target Membership

In case you can't see the image, it's a screenshot of the Xcode "Target Membership" pane. There are two targets: one with an "A" application icon and the test name. The other is the unit test target, and has a unit test icon:

Target Membership
[X] Foo
[ ] FooTests
Steve HHH
  • 12,947
  • 6
  • 68
  • 71
3

For me this happened because I deployed to the device and then to the simulator as I have NSZombies enabled. The solution was to switch to the simulator configuration & do a Product -> Clean then switch to the device configuration & do the same. Error went away. It's to do with build cache.

Geoff H
  • 3,107
  • 1
  • 28
  • 53
2

The reason is that you override RUNPATH_SEARCH_PATHS of your App Target`s build setting defined in other target.

Solution:

Go to your App Target and find RUNPATH_SEARCH_PATHS build setting and use there $(inherited) flag for both: Debug and Release

Bartłomiej Semańczyk
  • 59,234
  • 49
  • 233
  • 358
0

Come across the same issues, My situation is Class NSNotification is implemented in both /System/Library/Frameworks/Foundation.framework/Foundation, is there any dude come across the same issue, any direction or advise will be appriciated.

suxinde2009
  • 246
  • 2
  • 7
  • My problem was resolved, after struggle for a while, we found that one of the base lib our project denepended on introduce this bug, another team we cooperate rewrite the implementation of NSNotification, then they builded this code into an lib we depended on, finaly we came accross the issue I mentioned above. That sucks(真坑爹)!!! – suxinde2009 Aug 29 '18 at 07:26
  • plz help---objc[3108]: Class RTCCVPixelBuffer is implemented in both /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/WebCore.framework/Frameworks/libwebrtc.dylib (0x7fff898bce58) and /Users/thehe/Library/Developer/CoreSimulator/Devices/A592B792-DBE0-49ED-806D-E65192F01220/data/Containers/Bundle/Application/BC7FEC16-D134-401A-B051-B549C5B5664B/./Frameworks/WebRTC.framework/WebRTC (0x109651688). One of the two will be used. Which one is undefined – famfamfam Jan 06 '20 at 10:47