Ios 什么时候应用程序源需要包含在测试目标中?

Ios 什么时候应用程序源需要包含在测试目标中?,ios,objective-c,xcode,unit-testing,xctest,Ios,Objective C,Xcode,Unit Testing,Xctest,在一个新项目中,我有一个简单的测试 #import <XCTest/XCTest.h> #import "ViewController.h" @interface ViewControllerTests : XCTestCase @end @implementation ViewControllerTests - (void)testExample { // Using a class that is not in the test target. ViewC

在一个新项目中,我有一个简单的测试

#import <XCTest/XCTest.h>
#import "ViewController.h"

@interface ViewControllerTests : XCTestCase
@end

@implementation ViewControllerTests

- (void)testExample
{ 
    // Using a class that is not in the test target.
    ViewController * viewController = [[ViewController alloc] init];
    XCTAssertNotNil(viewController, @"");
}

@end
即使创建了新的XCTest单元测试目标,也会发生此链接器错误

为了解决这个问题,可以在应用程序和测试目标中同时包含源(在上图中勾选两个框)。这会在模拟器的系统日志中对重复符号发出生成警告(打开模拟器并按cmd-/查看此消息):

这些警告偶尔会导致以下示例所示的问题:

 [viewController isKindOfClass:[ViewController class]]; // = NO
 // Memory address of the `Class` objects are different.

 NSString * instanceClassString = NSStringFromClass([viewController class]);
 NSString * classString         = NSStringFromClass([ViewController class]);

 [instanceClassString isEqualToString:classString]; // = YES
 // The actual class names are identical
所以问题是旧项目中的什么设置要求在测试目标中包含应用程序源文件


评论摘要 在工作项目和非工作项目之间:

  • 链接器输出中没有差异(以
    Ld
    开头的命令)
  • 目标相关性没有差异(测试目标有1个相关性,即应用程序)
  • 链接器设置没有区别

  • 我花了一些时间弄明白这一点

    如果您阅读,您会发现Xcode有两种运行测试的模式。逻辑测试和应用程序测试。区别在于逻辑测试使用内置的类和符号构建自己的目标。生成的可执行文件可以在模拟器中运行,并将测试输出报告回Xcode。另一方面,应用程序测试构建一个动态库,链接到运行时注入应用程序的代码。这允许您在iPhone环境中运行测试,并测试Xib加载和其他事情

    由于在取消源文件链接时,测试目标中缺少符号,因此旧项目似乎已为逻辑测试而不是应用程序(单元)测试配置了测试目标

    现在,Xcode似乎是默认的创建一个应用程序测试目标,让我们了解所有可能需要更改的内容,以便将逻辑测试目标转变为单元测试目标


    我还将假设您有一个应用程序目标,而不是静态库目标,因为方向会有所不同

  • 在测试目标的生成设置中,删除“捆绑加载程序”和“测试主机”生成设置。稍后我们将使用Xcode添加这些内容
  • 您需要从测试目标中删除应用程序中的所有.m文件。您可以通过选择所有.m文件并在Xcode文件检查器中删除测试目标来完成此操作,也可以使用测试目标的编译源代码构建阶段
  • 更改测试目标的“框架搜索路径”。对于Xcode 5,它们应该是
    $(SDKROOT)/Developer/Library/Frameworks
    $(继承)
    $(开发人员\u框架\u目录)
    
    按该顺序和
  • 转到测试目标的构建设置的常规窗格,并从下拉菜单中选择目标。如果菜单已经指定了应用程序目标,则应将其关闭并再次打开。这将使Xcode重新配置捆绑加载程序,并使用正确的值测试主机设置
  • 最后,再次检查应用程序的方案。在“方案”下拉列表中,选择“编辑方案”。然后单击测试操作。确保您的测试目标位于“信息”窗格的列表中,并确保选择了所有测试 这些信息或多或少来自上面的链接文档,但我更新了Xcode 5的步骤

    编辑: Hmm 100%注意eph515所说的调试符号是可见的,但是您可能还想检查是否有人没有将您的方案的测试操作设置为在
    版本或其他配置中生成。单击方案选择器,然后选择编辑方案。单击测试操作,然后确保生成配置为
    Debug

    如果您有一个静态库目标 因此,如果您有一个静态库目标,则有两个选项: 1.逻辑测试 2.主机应用程序中的应用程序测试

    1。对于静态库目标,必须确保
    捆绑加载程序
    测试主机
    为空。然后必须将源代码编译到测试目标中,因为它们没有其他运行方式

    两个人。您需要在Xcode中创建一个新的应用程序项目,并将静态库项目添加为子项目。然后,您需要手动将
    捆绑加载程序
    测试主机
    构建设置从新应用的测试目标复制到静态Lib测试目标。然后,打开新测试应用程序的方案,并将测试目标添加到新应用程序的测试操作中。
    要在lib上运行测试,请运行主机应用程序的测试操作

    我也遇到了这个问题,并遵循了jackslash的建议,但还有一个补充:选择主目标并查找默认隐藏的符号(在Apple LVM 5.0-代码生成下),如果值为“是”,则将其更改为“否”。这似乎“取消隐藏”了单元测试目标正在查找的编译源代码的所有符号。对我有用。请确保包括jackslash概述的所有步骤。

    答案是jackslash和eph515答案的组合

    在eph515的回答中,默认情况下隐藏的符号对于调试来说应该是否定的

    另外,
    部署后处理
    对于调试来说也应该是不允许的

    此外,测试目标中包含的所有库都应该从单元测试中删除。剩下的应该是屏幕截图中的3加上任何特定于单元测试的内容

    另外,如果在列表的末尾有一个运行-构建脚本-构建阶段,那么应该删除它(因为它是单元测试的产物)


    然后在中执行所有操作。

    在Xcode 6上,我可以通过检查测试目标>常规>测试中的“允许测试主机应用程序API”来解决此问题


    在我的例子中,Xcode 6.2在项目目标和te的不同架构中出现了错误
    Class ViewController is implemented in both 
    [...]/iPhone Simulator/ [...] /MyApp.app/MyApp and 
    [...]/Debug-iphonesimulator/LogicTests.octest/LogicTests. 
    One of the two will be used. Which one is undefined.
    
     [viewController isKindOfClass:[ViewController class]]; // = NO
     // Memory address of the `Class` objects are different.
    
     NSString * instanceClassString = NSStringFromClass([viewController class]);
     NSString * classString         = NSStringFromClass([ViewController class]);
    
     [instanceClassString isEqualToString:classString]; // = YES
     // The actual class names are identical
    
    Project Editor -> Project Tests target -> Build Settings -> Valid Architectures = armv7 armv7s
    
    General -> Host  Application <app_name> -> >check< Allow testing Host Application APIs 
    
    Undefined symbol: nominal type descriptor for <class_name>
    Undefined symbol: type metadata accessor for <class_name>