Cocoapods: Link Pods Headers to Test Target

Created on 26 Sep 2013  路  17Comments  路  Source: CocoaPods/CocoaPods

I use some libraries from my main target in my test target too. This leads to a major problem:

  1. If I use a plain Podfile, the headers of this libs cannot be found from the test target
  2. If I follow http://stackoverflow.com/a/17885961/354018 and add link_with ['MainTarget', 'MainTargetTests'] to my Podfile, the test target compiles fine, starts running, but crashes due to the following problem. I namely use the Mantle pod. the app crashes even before the tests start, but in the main target's applicationDidBecomeActive: method. There, a Mantle class is trying to be created which crashes in an assertion where isSubclassOfClass: fails, even when the tested client class _is_ a subclass of the expected Mantle model class. The only way I can explain this strange error (that does not happen when running the app regularly), is that both the main target and the test target link the library dependency, and when the test target loads the main target via bundle loader, the Mantle classes from the test target overrule the ones from the main target. The failing isSubclassOfClass: method compares a main target's class (which is a subclass of the main target's Mantle library model class) with the Mantle library model class - but I guess at this point the test target's Mantle library model class is taken.

... it's hard to explain, when I have some time I can try to create a minimal demonstrating project.

_Workaround:_ The only thing that helped, was _not_ to use link_with ['MainTarget', 'MainTargetTests'] and add this path to the Header Search Paths of the test target: "$(PROJECT_DIR)/[..]/Pods/BuildHeaders" (recursively).

Most helpful comment

After thousands of builds and modifications of Podfile and project settings. Finally it work for me.

Just remember to set MyProjectTest : Build Settings -> Objective-C Bridging Header to Empty while MyProject: Build Settings -> Objective-C Bridging Header point to the bridging header file.

My PodFile as below:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios,'9.0'
use_frameworks!

target 'MyProject' do

    #all pods required by MyProject here

    target 'MyProjectTest' do
        inherit! :search_paths
    end
end

pod: 1.1.1
Xcode :8.1

All 17 comments

Currently we have a workaround for that which is to mark the target as exclusive. Sadly this important area of CocoaPods has been neglected for a while. To learn more about what needs to be done to properly support this please see and share your thoughts in #840.

@fabb Can you post a snippet of your working Podfile? I'm struggling with exactly the same issue and am trying to find a workaround.

@mhupman I did not solve this by a Podfile hack yet, I'm still (happily) using the workaround described in my question above.

@fabb Do you have a separate target definition in your Podfile for your testing target? My Podfile looks as follows:

platform :ios, '6.1'

pod 'RestKit'

target :AppTests, :exclusive => true do
    pod 'Kiwi/XCTest'
    pod 'RestKit/Testing'
end

With this setup, my testing target is already including all of the Pod headers (provided by the Pods-AppTests.xcconfig file), which is why I was curious about your setup when you mentioned what your workaround was.

No, I don't yet have that in my Podfile.

The crash in Mantle is because there are effectively two copies of Mantle loaded at runtime because of the way that the main target is injected into the test target at runtime.

The crash is because it is comparing one version of a class against another with the same name (but is not the same, thus the assertion fails).

To fix this, you prevent two copies of any dependency from being loaded. There are two ways to do this, one is with exclusive targets and one being with separate targets.

pod 'RestKit'

target :AppTests, :exclusive => true do
    pod 'Kiwi/XCTest'
    pod 'RestKit/Testing'
end

or

target 'App'
  pod 'RestKit'
end

target :AppTests do
    pod 'Kiwi/XCTest'
    pod 'RestKit/Testing'
end

I still see this problem and I don't have any test target specific pods. None of my other pods depend on Mantle, so I'm not sure why it injects two versions of Mantle at runtime. The only solution that is working for me is to import the headers using relative paths like OP.

I managed to fix it by adding a pod to the test target that I don't even use. It seems that solves the problem by adding the necessary header files to my project through the .xcconfig files:

target :Target do
pod 'some-required-pod'
...
end

target :TargetTests do
pod 'some-pod-not-even-used'
end

Hi! Do you know if this is the way to solve it still? Or is there any other solution more elegant than use a no needed pod?

@daniel-reckoder :
The solution I currently use came from @kylef earlier in the thread:

target :"MyAppName Tests", :exclusive => true do
    # this pod is not used by MyAppName Tests, but we need something here to ensure we properly link cocoapods to the MyAppName Tests target
    # this dummy pod is not part of kylef's solution, but since we don't have anything unique to our test target, I added it here to ensure something unique was present
    pod 'OCMockito'
end

@karlbecker:
That's my current solution as well. I was wondering about a new one where we don't have to use a pod that we don't need. Thanks, anyway!

Any updates about this? Thanks!

Can anybody document the CORRECT way to do this?

Documentation at https://guides.cocoapods.org/syntax/podfile.html#podfile only says "An example of a more complex Podfile can be:" and posts something that doesn't apply to me.

Then later, "Defining a test target which can access SSZipArchive via Pods" describes a solution.

But when implemented, that produces the error:

Invalid Podfile file: undefined method `inherit!'

Of course in this issue, and https://github.com/CocoaPods/CocoaPods/issues/1411, there is discussion of other people that are doing it the wrong way, apparently.

I thought setting up a Test target would be simple, but I find this is woefully wrong.

FYI, for anyone else who came here looking to set up a Podfile for an application target AND a test target, the following works for me using cocoapods 0.39. It produces lots of stupid error message which I ignore and seemingly have no consequence. Note: this is for a Swift project which also has (some) Swift pods.

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
use_frameworks!

target 'Echo' do
    pod 'Alamofire'
    pod 'AFNetworking'
    pod 'Google/Analytics'
    pod 'Google/AppInvite'
    pod 'Appirater'
    pod 'FDTake'
    pod 'MBProgressHUD'
    pod 'NSData+Base64'
    pod 'TDBadgedCell'
    pod 'FDWaveformView'
    pod 'SwiftyJSON'
end

target 'EchoTests' do
    pod 'Alamofire'
    pod 'AFNetworking'
    pod 'Google/Analytics'
    pod 'Google/AppInvite'
    pod 'Appirater'
    pod 'FDTake'
    pod 'MBProgressHUD'
    pod 'NSData+Base64'
    pod 'TDBadgedCell'
    pod 'FDWaveformView'
    pod 'SwiftyJSON'
end

@fulldecent, you can do something like this so you don't have to repeat the pods.

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
use_frameworks!

def all_pods
    pod 'Alamofire'
    pod 'AFNetworking'
    pod 'Google/Analytics'
    pod 'Google/AppInvite'
    pod 'Appirater'
    pod 'FDTake'
    pod 'MBProgressHUD'
    pod 'NSData+Base64'
    pod 'TDBadgedCell'
    pod 'FDWaveformView'
    pod 'SwiftyJSON'
end

target 'Echo' do
    all_pods
end

target 'EchoTests' do
    all_pods
end

_However, this didn't work for me_. I had to remove the test target altogether from the Podfile and set the header search paths of the test target using a config file:

PODS_ROOT = ${SRCROOT}/../Pods
HEADER_SEARCH_PATHS = "${PODS_ROOT}/leveldb-library/" "${PODS_ROOT}/leveldb-library/include" $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" "${PODS_ROOT}/Headers/Public/CorePlot" "${PODS_ROOT}/Headers/Public/Crashlytics" "${PODS_ROOT}/Headers/Public/DateTools" "${PODS_ROOT}/Headers/Public/Fabric" "${PODS_ROOT}/Headers/Public/IBActionSheet" "${PODS_ROOT}/Headers/Public/KiipSDK" "${PODS_ROOT}/Headers/Public/Mixpanel" "${PODS_ROOT}/Headers/Public/Objective-LevelDB" "${PODS_ROOT}/Headers/Public/SOCKit" "${PODS_ROOT}/Headers/Public/SSPullToRefresh" "${PODS_ROOT}/Headers/Public/UrbanAirship-iOS-SDK" "${PODS_ROOT}/Headers/Public/ViewDeck" "${PODS_ROOT}/Headers/Public/leveldb-library" "${PODS_ROOT}/Headers/Public/libzmq" "${PODS_ROOT}/Headers/Public/youtube-ios-player-helper"

You also have to make sure you are not linking against the pod library and that there aren't any pod build phases in your test target. As I understand it, the pods are already included in your app target. You don't need to include them again in your test target. But your test target needs to know where your header files are.

Here are four of our Artsy Podfiles: Eigen, Energy, Eidolon and Emergence. All are still on 0.39 syntax. All work fine with extensive test suites. The guides are currently working from the 1.0.0b which has a more explicit syntax for this stuff.

Can people please not bring old threads back to life, this discussion probably should have been on StackOverflow.

After thousands of builds and modifications of Podfile and project settings. Finally it work for me.

Just remember to set MyProjectTest : Build Settings -> Objective-C Bridging Header to Empty while MyProject: Build Settings -> Objective-C Bridging Header point to the bridging header file.

My PodFile as below:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios,'9.0'
use_frameworks!

target 'MyProject' do

    #all pods required by MyProject here

    target 'MyProjectTest' do
        inherit! :search_paths
    end
end

pod: 1.1.1
Xcode :8.1

Was this page helpful?
0 / 5 - 0 ratings