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

#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

视图控制器.h是测试目标的not部分,但它编译并运行测试时没有任何问题.

在此处输入图像描述

我认为这是因为应用程序首先构建(作为一种依赖性),然后才进行测试.然后链接器计算出ViewController类是什么.

但是,在一个旧项目中,使用完全相同的测试和ViewController文件,构建在链接器阶段失败:

Undefined symbols for architecture i386:
"_OBJC_CLASS_$_ViewController", referenced from:
  objc-class-ref in ViewControllerTests.o

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

要解决此问题,可以将源代码同时包含在应用程序和测试目标中(勾选上图中的两个框).这会导致模拟器的系统日志(log)中出现重复符号的构建警告(打开模拟器并按cmd-/查看):

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

因此,问题是旧项目中的哪些设置要求将应用程序源文件包括在测试目标中?


comments 摘要

在工作项目和非工作项目之间:

  1. 链接器输出没有区别(命令以Ld开始).
  2. 目标依赖性没有差异(测试目标有1个依赖性,即应用程序)
  3. 链接器设置没有区别.

推荐答案

我花了一些时间弄清楚这一点.

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

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

现在Xcode似乎是trying not to distinguish between the two,并且默认创建一个Application Tests目标,让我们演练一下将逻辑测试目标转换为单元测试目标可能需要更改的所有内容.

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

  1. 在测试目标的生成设置中,删除"Bundle Loader"和"Test Host"生成设置.我们稍后会让Xcode将这些添加回来
  2. 您需要从测试目标中删除应用程序中的所有.m文件.您可以通过 Select 所有.m文件并在Xcode文件判断器中删除测试目标来完成此操作,也可以使用测试目标的编译源代码构建阶段.
  3. 更改测试目标的"框架搜索路径".对于Xcode5,它们应该是 按照这个顺序$(SDKROOT)/Developer/Library/Frameworks $(inherited) $(DEVELOPER_FRAMEWORKS_DIR) with no extra quotes or backslashes
  4. 转到测试目标的构建设置的General窗格,并从下拉菜单中 Select 您的目标.如果菜单已经指定了您的应用程序目标,您应该将其关闭并再次打开.这将使Xcode使用正确的值重新配置Bundle 加载器和测试主机设置.
  5. 最后,仔细判断你的申请方案.在方案下拉列表中 Select 编辑方案.然后单击测试操作.确保您的测试目标位于信息窗格的列表中,并确保 Select 了所有测试.

这些信息或多或少来自上面的链接文档,但是我更新了Xcode5的步骤.

编辑:

嗯,Release%注意到eph515所说的调试符号是可见的,但您可能还想判断一下,是否有人没有将您的方案的测试操作设置为在Release或其他配置中进行构建.单击方案 Select 器,然后 Select 编辑方案.单击测试操作,然后确保构建配置为Debug

为方案中的测试操作生成配置屏幕

如果你有一个静态库目标

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

对于1,您必须确保对于您的静电库目标,Bundle Loader%和Test Host%是空的.然后,您的源代码必须编译到测试目标中,因为它们没有其他运行方式.

对于2.您需要在Xcode中创建一个新的APP项目,并将您的静电库项目添加为子项目.然后,您需要手动将第Bundle Loader和第Test Host个构建设置从新应用程序的测试目标复制到静电库测试目标.然后打开新测试应用程序的方案,并将测试目标添加到新应用程序的测试操作中. 要在库上运行测试,您需要对主机应用程序运行测试操作.

Ios相关问答推荐

如何保持拖动预览与原始项目完全相同?

缺少预期的键:';NSPrival yCollectedDataTypes';

PieChartView未以编程方式显示在屏幕上

Firebase SDK 10+—无法安装软件包

如何创建自定义组件来接受(和传递)所有可能的Textfield参数?

在带有可编码的SWIFT5中,可以轻松处理以美元符号开头的JSON密钥,例如$DATE";

无法下载并安装带有Xcode 15.0的iOS 17.0模拟器运行时

快速 keys 串更新只有在第二次try 时才起作用

Swift - 缺少函数参数在编译期间不会失败,甚至会导致不同的数据类型?

如何通过点击按钮滚动到 ScrollView 中的特定视图?

动画文本位置

Flutter Aging Person 自定义小部件

Flutter firebase_auth 中的无效应用程序凭据/令牌不匹配

如何在 SwiftUI 中创建两个大小相同的正方形占据整个屏幕宽度?

在视图控制器中实例化视图控制器是否可能/智能?

SwiftUI - 使用切换switch 切换键盘类型(默认/数字)?

从 WKWebView 获取所有 cookie

~= Swift 中的运算符

命令 /Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang 失败,退出代码为 1

Xcode / iOS模拟器:手动触发重大位置更改