将CocoaPods与iOS逻辑测试一起使用时找不到库


148

我正在尝试针对一些项目中的类编写一些iOS逻辑测试,这些测试使用了podspec中某些库的功能。我正在使用Xcode中提供的标准单元测试包(尽管不是Application Tests,只是单元测试)。

例如,我使用Magical Record,并在podspec中链接了该库。它存在于我的工作区的Pods项目中,并且可以在模拟器或设备上运行该应用程序时按预期工作。但是,当我尝试链接到使用Magical Record的对象的测试时,出现链接器错误,指出它无法从Magical Record中找到选择器。我曾尝试在逻辑测试包中更新HEADER_SEARCH_PATH,甚至将其硬编码到CocoaPods创建的标头目录中,但没有运气。

我可以毫无问题地对不使用CocoaPods库的类进行单元测试。

我要解决这个错误吗?我是否应该做其他事情来让编译器查看CocoaPods库?

Answers:


224

CocoaPods 1.0更改了此语法。现在看起来像这样:

def shared_pods
    pod 'SSKeychain', '~> 0.1.4'
    ...
end

target 'Sail' do
    shared_pods
end

target 'Sail-iOS' do
    shared_pods
end

Pre CocoaPods 1.0答案

您要使用的link_with来自您的Podfile。就像是:

link_with 'MainTarget', 'MainTargetTests'

然后pod install再次运行。


7
这立即为我解决了问题。
mttrb

9
我遇到了奇怪的错误-测试时,isSubclassOfClass:调用会返回NO他们应该返回的位置YES。我能解释的唯一原因是,依赖项确实链接到主对象和测试目标,并且当测试目标的包加载器加载主包时,它无法决定采用哪个类。

4
我有同样的问题isKindOfClass:返回NO时,它应该返回YES。如果将指向Class要测试的对象的指针记录到日志中,而Class要与之进行比较的类的指针是两个不同的值。显然,我的应用程序捆绑包中的代码为类使用的符号与单元测试中的代码不同。有没有人找到解决这个问题的方法?
尼古拉斯·哈特

2
由于其他人提到的错误,我认为这不是一个好方法。坚持更新“基于”位的配置文件。确保您没有两次链接libPods.a。
Bob Spryn 2014年

3
这应该是可以接受的答案,因为这是CocoaPods的官方方法,可以设置具有多个目标的Pod。非常感谢基思!
cschuff 2014年

174

我通过查看应用程序的主要目标是如何从CocoaPods库接收设置来弄清楚这一点的。CocoaPods包含一个名为Pods.xcconfig的.xcconfig文件。该文件包含所有标题搜索路径。

如果在项目导航器中查看项目,然后单击“信息”选项卡,则将在顶部列出构建配置。如果您为不同的配置打开显示三角形,则将在主要目标下看到Pod。我必须单击下拉列表,并将Pods也添加到逻辑测试目标。

配置快照

我也有复制的设置$(inherited),并${PODS_HEADERS_SEARCH_PATHS}从我的主要目标,并在构建设置下将它们复制到逻辑测试目标/ HEADER_SEARCH_PATHS。

最后,我必须为逻辑测试目标在与库的链接二进制文件构建阶段中添加libPods.a。

希望这可以帮助其他人。


辉煌!我使用MagicalRecord以及OCMockito和OCHamcrest进行单元测试。有了此修复程序,我现在可以通过CocoaPods全部安装它们了!谢谢!
Fogmeister

4
这对我有用,谢谢。注意:我不需要将libPods.a添加到测试项目和主项目中。这会导致重复的符号错误
Craig Bruce

对于我来说,我还必须复制“用户定义”的构建设置。标头搜索路径引用未在测试目标上定义的$ PODS_ROOT。您可以通过以下方法添加它:转到编辑器->添加构建设置->添加用户定义的设置,然后从主目标复制$ PODS_ROOT值。
Shinigami 2014年

11
这不是解决此问题的正确方法。参见带有link_with的答案。您还可以在Pod文件中基于每个目标指定不同的Pod ,即,仅在测试目标中包括OCMockito。
dbainbridge

对对对!在此答案之前,我必须从我的项目中删除测试目标!谢谢男人:)
Josip B.

53

我在这里找到一种解决方案,使用CocoaPods进行单元测试

在Xcode中打开项目文件,然后选择Project(不是目标),在右侧面板中有一个名为Configurations的部分。在“基于配置文件”列中为您的测试目标选择Pod。

在此处输入图片说明


好吧,如果存在特定于测试的依赖关系,例如Specta您要与测试项目而不是主项目链接,该怎么办?:S
fatuhoku

这可以正常工作,不需要对Pod的配置或设置进行任何更改。
理查德

1
尽管此解决方案可能会产生错误:Class Foo is implemented in both MyApp and MyAppTestCase. One of the two will be used. Which one is undefined. 这似乎是由于Cocoapods中的错误引起的;请参阅下面的@JRV答案。
理查德

这些不只是警告。通过这种设置,在大多数情况下,不会生成适当的Xcode代码覆盖率数据,并且单元测试只会在启动期间挂起。
i4niac

我已经通过拖放操作手动导入了Estimote SDK,但没有得到Pod。如何解决呢?
Guru Teja

18

我同意其他答案,即有必要将库链接到测试目标。但是,到目前为止,所有建议都没有帮助我。正如@fabb在评论中写道:“在测试时,isSubclassOfClass:调用在应返回YES的地方返回NO。我能解释的唯一原因是,依赖项确实链接到主对象和测试目标,以及测试目标的捆绑包加载程序加载主程序包,它无法决定采用哪个类。” 在此线程中,所有先前的建议都遇到相同的问题。

我要工作的解决方案是更新Podfile,以为我的主要目标和测试目标定义特定的Pod:

target 'MyTarget' do
   pod 'AFNetworking', '~> 2.5.0'
   pod 'Mantle', '~> 1.5'
end

target 'MyTargetTests' do
   pod 'OCMockito', '~> 1.3.1'
end

即使我没有使用任何特定于测试的Pod ,也有必要为我的测试目标指定Pod。否则,CocoaPods不会在我的项目中插入必要的链接逻辑。

这个链接帮助我得出了这个结论。


1
感谢您链接到CocoaPods问题-帮助我解决了问题!
karlbecker_com 2015年

是!!!!这个问题困扰着我。这是我遇到的唯一明智的cocoapods答案。
DonnaLea

在1.x下有更好的处理方法:stackoverflow.com/a/40866889/2799670
Darren Black

6

我添加了:exclusive => true以避免在应用程序测试目标中出现重复的符号错误。

target 'myProjectTests', :exclusive => true do
   pod 'OCMock', :head
   pod 'XCTAsyncTestCase', :git => 'https://github.com/iheartradio/xctest-additions.git'
end

link_with 'myProject', 'myProjectTests'

当我将应用程序测试目标更改为逻辑单元测试1时,发生链接器错误。删除后:exclusive => true,一切都会恢复。

target 'myProjectTests', do
   pod 'OCMock', :head
   pod 'XCTAsyncTestCase', :git => 'https://github.com/iheartradio/xctest-additions.git'
end

link_with 'myProject', 'myProjectTests'

:exclusive => true声明do...end不应将外部所有内容链接到myProjectTests,这在应用程序测试目标中是合理的,但会导致逻辑测试目标中的链接器错误。


kylef在此CocoaPods问题上的回答所示,该解决方案对我来说是唯一的,这要归功于JRV在此问题上的回答!
karlbecker_com 2015年

1
是的,每个人都应该在@karlbecker_com链接的github上阅读该问题。似乎这只是可可足类的长期限制。根据那里的讨论,link_with是不必要的。只需添加测试目标并使用:exclusive。如果您的测试目标不需要任何特定的Pod,则无论如何都添加一个,否则cocoapods不会对其进行处理。
kball 2015年

@kball哪个不需要link_with?应用测试还是逻辑单元测试?
Hai Feng Kao 2015年

除非有其他原因要使用它,否则根本不需要link_with。通常来说,您不想将这些Pod与测试包链接。它们仅应在应用程序捆绑包中链接一次,然后由您的测试通过依赖项进行引用(确保关闭默认隐藏的符号)。否则,行为是不确定的,因为将存在Pod的两个版本-一个包含在应用程序目标中,一个包含在测试目标中。
kball

6

您可以根据@Keith Smiley解决方案使用link_with。

如果您有通用的广告连播,并且每个目标都有具体说明,则可能需要使用“ def”选项来定义广告连播组。并在以后的专有目标中使用“ def”。

def import_pods
    pod 'SSKeychain'
end

target 'MyProjectTests', :exclusive => true do
  import_pods
end

target 'MyProject', :exclusive => true do
  import_pods
  pod 'Typhoon'
end

在上面的示例中,我向两个目标都添加了“ SSKeychain”,并且仅向“ MyProject”目标中添加了“台风”


5

我对这个问题的解决方案是更改我的Podfile,使其在两个目标中都包含该库,如下所示

target "MyApp" do  
    pod 'GRMustache', '~> 7.0.2'
end

target "MyAppTests" do
    pod 'GRMustache', '~> 7.0.2'
end

由于使用的是swift,因此我还必须配置测试目标以包含MyApp-Bridging-Header.h文件。(在“构建设置”选项卡下的“ Swift编译器”组中)


3
小心-随着您不断添加更多Pod,这将大大增加您的构建时间!
fatuhoku 2015年

@fatuhoku不知道。您能提供一些为什么会增加构建时间的见解吗?
Qw4z1

2
好吧,每次提及吊舱都是您Pods项目中的目标。两次提及Pod(一次用于测试,一次针对应用),您将拥有两组目标。这实际上使配置工作加倍pod install。除非您有> 15个吊舱,否则这不会成为问题,因此在此之前不要担心太多。
fatuhoku 2015年

1
这是唯一适用于Cocoapods 1.0的解决方案
William Entriken,2016年

从1.x版本开始,这是继承应用程序依赖项的正式测试方法:stackoverflow.com/a/40866889/2799670
Darren Black

4

当我在某些版本控制期间丢失了一些库文件时,发生了类似的情况。我仍然在Pods中看到该库文件,但是缺少实际的代码,XCode说它不见了。令我沮丧的是,运行“ pod安装”并没有立即将丢失的文件重新带回。

我必须通过执行以下操作手动删除和更换吊舱:

  1. 从Podfile中删除库
  2. 运行“ pod install”以完全删除该库
  3. 将库放回Podfile
  4. 再次运行“ pod install”

这应该将有问题的库恢复为原始形式。


2

还值得注意的是,如果您libPods.a添加了两次,将会得到一些讨厌的错误,如下所示:

232 duplicate symbols for architecture i386

要解决此问题,只需libPods.a在Project Explorer中删除其中一个引用即可。


2

从CocoaPods 1.x开始,有一种新方法可以声明目标和相应测试目标之间的共享依赖关系。到目前为止,我一直在使用Mark Struzinski接受的解决方案,但是在运行测试时,使用此方法会产生大量警告:

Class SomeClass is implemented in both /Path/To/Test/Target and /Path/To/App/Target. One of the two will be used. Which one is undefined.

使用CocoaPods 1.x,我们可以声明-Test目标是通过父目标的搜索路径继承的,如下所示:

target 'MyApp' do
    pod 'aPod'
    pod 'anotherPod'
    project 'MyApp.xcodeproj'
end
target 'MyAppTests' do
    inherit! :search_paths
    project 'MyApp.xcodeproj'
end

这将导致-Test目标可以访问应用程序目标的依赖项,而无需多个二进制副本。这为我大大加快了测试构建时间。



1

我正在使用我的Swift应用程序在iOS上使用GoogleMaps Objective-C POD集成,因此对我来说,问题是测试目标在构建设置中未引用桥头文件(SWIFT_OBJC_BRIDGING_HEADER)。确保您的应用程序和测试应用程序目标均指向该目标,以便可以在快速单元测试中使用第三方API调用(地图API等)。


1
我的设置与您类似。我已经将桥接标头添加到测试目标,但是在上出现错误“ No such module'GoogleMaps'” import GoogleMaps
Nicolas Miari '16

0

下一种语法对我来说是最好的结果(在cocoapod v.1.2.1下测试):

https://github.com/CocoaPods/CocoaPods/issues/4626#issuecomment-210402349

 target 'App' do
    pod 'GoogleAnalytics' , '~> 3.0'
    pod 'GoogleTagManager' , '~> 3.0'

     pod 'SDWebImage', '~>3.7'
     platform :ios, '8.0'
     use_frameworks!

     target 'App Unit Tests' do
         inherit! :search_paths
     end
 end

没有这个,我会在测试运行中有关于重复符号的警告。

此后警告消失。


0

我在XCTest下使用OpenCV时遇到问题。它给了Undefined symbols for architecture arm64像的类的链接器错误cv::Mat。我通过pod 'OpenCV', '~> 2.0'在主要目标下使用CocoaPods安装OpenCV 。无论我多么努力地尝试将OpenCV依赖项置于测试目标之下,或者inherit! :search_paths都没有使用它。解决方案是创建一个abstract_target像这样的:

# Uncomment the next line to define a global platform for your project
platform :ios, '6.1.6'

abstract_target 'Shows' do
  pod 'RMVision', path: '../..'
  pod 'RMShared', path: '../../../RMShared'
  pod 'OpenCV', '~> 2.0'

  target 'RMVisionSample' do
    # Uncomment the next line if you're using Swift or would like to use dynamic frameworks
    # use_frameworks!

    # Pods for RMVisionSample
  end

  target 'RMVisionSampleTests' do
    # inherit! :search_paths
    # Pods for testing
  end

  target 'RMVisionBenchmarks' do
    # inherit! :search_paths
    # Pods for testing
  end

end 

pod deintegratepod clean命令也很有用,可帮助清理项目并确保在测试时重新启动。您可以使用安装这两个[sudo] gem install cocoapods-deintegrate cocoapods-clean

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.