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

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

#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 是测试目标的 没有部分,但它编译并运行测试时没有任何问题。

enter image description here

我认为这是因为应用程序首先被构建(作为一个依赖项) ,然后才是测试。然后,链接器会计算出 ViewController 类是什么。

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

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

即使创建了新的 XCTest 单元测试目标,也会出现此链接器错误。

为了解决这个问题,可以在应用程序和测试目标中同时包含源代码(在上图中的两个框中打勾)。这会导致在模拟器的系统日志中出现重复符号的构建警告(打开模拟器并按 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

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


意见摘要

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

  1. 链接器输出(以 Ld开头的命令)没有区别。
  2. 目标依赖关系没有区别(对测试目标有1个依赖关系,即应用程序)
  3. 链接器设置没有差异。
24034 次浏览

我花了点时间想明白。

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

因为当您断开源文件的链接时,您的测试目标中缺少了这些符号,所以您的旧项目似乎配置了一个用于逻辑测试的测试目标,而不是 Application (单元)测试。

由于目前 Xcode 似乎是 尽量不去区分这两者,并且默认创建应用程序测试目标,因此我们可以遍历所有可能需要更改的内容,以便将 Logic Test Target 转换为单元测试目标。

我还假设您有一个 Application Target,而不是一个静态库目标,因为方向会有一点不同。

  1. 在测试目标的生成设置中,删除“ Bundle Loader”和“ Test Host”生成设置。我们稍后将让 Xcode 将这些内容添加回来
  2. 你需要移除所有。来自测试目标的应用程序的 m 个文件。可以通过选择所有。M 文件并删除 Xcode File 检查器中的测试目标,或者您可以使用测试目标的编译源代码构建阶段。
  3. 更改测试目标的“ Framework 搜索路径” $(SDKROOT)/开发人员/库/框架 $(继承) $(开发者 _ 框架 _ DIR) 按这个顺序和 没有额外的引号和反斜杠
  4. 转到测试目标的生成设置的“常规”窗格,并从下拉菜单中选择目标。如果菜单已经指定了您的应用程序目标,您应该再次关闭和打开它。这将使 Xcode 使用正确的值重新配置 Bundle 加载程序和 Test Host 设置。
  5. 最后再次检查应用程序的方案。在方案下拉菜单中选择编辑方案。然后单击测试操作。确保您的测试目标在信息窗格的列表中,并确保选择了所有测试。

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

编辑:

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

build configuration screen for test action in a scheme

如果您有一个静态库目标

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

一人份。对于静态库目标,必须确保 Bundle LoaderTest Host是空的。然后,必须将源编译到测试目标中,因为它们没有其他运行方式。

两个人。你需要在 Xcode 创建一个新的应用程序项目,并将静态图书馆项目作为子项目添加进来。然后,您需要手动将 Bundle LoaderTest Host构建设置从 New App 的测试目标复制到 StaticLib 测试目标。然后打开新 Test App 的方案,并将测试目标添加到新应用程序的测试操作中。 要在库上运行测试,需要运行宿主应用程序的测试操作。

我也遇到了这个问题,并且遵循了 jackslash 的建议,但是还有一个附加的问题: 选择你的主要目标,查找默认隐藏的符号(在 Apple LVM 5.0-Code Generation 下) ,如果值是 Yes,将它改为 No。这似乎“解除”了单元测试目标正在寻找的已编译源代码的所有符号的隐藏。我没问题。请确保您包括所有的步骤,杰克逊概述以及。

答案是 Jackslash 和 eph515的答案的组合。

如在 eph515的答案 symbols hidden by default应该是否调试。

enter image description here

对于调试,deployment postprocessing也应该是否定的。

enter image description here

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

enter image description here

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

然后用 Jackslash 的回答做所有的事情。

在 Xcode 6中,我能够通过在测试目标 > 常规 > 测试中检查“允许测试主机应用程序 API”来修复这个问题。

Xcode Screenshot

在我的案例中,Xcode 6.2在项目目标和测试目标的不同体系结构中是错误的。

Project target 只有 armv7和 armv7s 架构(因为一些旧的库)

项目测试目标有 armv7,armv7和 arm64架构。

移除 arm64架构为我的案例解决了这个问题。

Project Editor -> Project Tests target -> Build Settings -> Valid Architectures = armv7 armv7s

(也许还需要将“ Architecture”而不是 $(ARCHS _ STANDARD)设置为 $(ARCHS _ STANDARD _ 32 _ BIT))

对我来说,这只是一个没有为 Scheme 添加任何测试目标的例子。

对于应用程序目标,转到 Edit Scheme,然后在右侧单击 Test,然后添加一个测试目标,并在底部添加 + 按钮: enter image description here

当您为测试应用程序创建 Unit Testing Bundle(单元测试目标)时,您有两个选项

  1. 启用 Allow testing Host Application APIs
General -> Host  Application <app_name> -> >check< Allow testing Host Application APIs
  • 慢慢来
  1. 将每个应用程序的测试文件添加到 Target Membership< sup > [ About ]
  • 你应该注意那些用于可测试类的类依赖关系(它们也应该被添加)

当您编写测试并且没有一个选项未启用时,您可以获得

Undefined symbol: nominal type descriptor for <class_name>
Undefined symbol: type metadata accessor for <class_name>