35

我正在将我的项目从 Xcode 4.6.3 迁移到 Xcode 5.0.2。该项目的单元测试是使用 SenTestingKit/OCUnit 开发的。现在,当我在 Xcode 5 中运行测试时,我从RunUnitTests脚本中得到一个错误,告诉我

RunUnitTests 已过时。

可能相关的是 Xcode 5 发行说明中的​​此说明:

SenTestingKit 和 OCUnit 已弃用。使用迁移器移动到 XCTest。

不幸的是,我无法找到更多关于这个神秘“移民”的信息。可能我的 google-fu [再次] 缺少,所以我的主要问题是:如何将单元测试从 SenTestingKit/OCUnit 迁移到新的 XCTest(有或没有“迁移器”)?

第二个问题,如果迁移是一项复杂的业务:是否有可能让 Xcode 5 运行仍然基于 SenTestingKit/OCUnit 的单元测试?毕竟这些只是被弃用了,所以它们应该仍然存在并且可以使用。

4

2 回答 2

59

感谢 Shaggy Frog 的回答,我们知道 Xcode 发行说明中提到的神秘“迁移器”是通过选择“编辑 > 重构 > 转换为 XCTest”启动的向导。我将分两部分写下我对这个向导的体验。第一部分是对主要问题的不完整回答,第二部分回答次要问题。


第 1 部分:如何从 OCUnit 迁移到 XCTest

您需要意识到的第一件事是,要使向导工作,您需要选择一个单元测试目标。如果您选择了主要目标,则向导不会列出任何要转换的目标。

一旦我发现了这一点,我就能够逐步完成向导,但就我而言,最终结果仍然是一个惊人的失败!该向导声称不需要更改源代码,只需更新构建设置即可迁移到 XCTest。最后,向导甚至没有正确地做到这一点:它确实删除了对 SenTestingKit 框架的引用,但它没有添加对 XCTest 框架的引用。

无论如何,以下是我必须手动进行的更改列表,因为向导未能为我进行更改。如果向导更适合您,您可能不需要执行所有这些操作。

  1. 从单元测试目标中删除“运行脚本”构建阶段
  2. 将所有测试用例类的基类从SenTestCase更改为XCTestCase
  3. 将导入的标头从更改<SenTestingKit/SenTestingKit.h><XCTest/XCTest.h>
  4. 在测试目标的 Build Settings 中,将 Wrapper Extension 从 更改octestxctest.
  5. 将所有断言宏从ST*to重命名XCT*(例如STAssertTrue变成XCTAssertTrue
  6. 上述例外:STAssertEquals需要重命名为XCTAssertEqual(注意末尾缺少的“s”)。如果您收到此编译器警告,您将知道您已经忘记了这一点:warning: implicit declaration of function 'XCTAssertEquals' is invalid in C99
  7. 新的 XCTest 断言宏不允许nil作为故障描述传递。例如,XCTAssertNotNil(anObject, nil)不可能,必须更改为XCTAssertNotNil(anObject). 当你得到这个编译器错误时,你就会知道你有这个问题:error: called object type 'NSString *' is not a function or function pointer.
  8. 如果您确实需要传递失败描述,新的 XCTest 断言宏需要格式说明符的常量表达式,就像NSString类方法stringWithFormat:一样。当你得到这个编译器错误时,你就会知道你有这个问题:error: expected ')'. 一些例子:
NSString* formatSpecifier = @"%@";
NSString* failureDescription = @"foo";
// These are OK
XCTAssertNotNil(anObject, @"foo")
XCTAssertNotNil(anObject, @"%@", failureDescription)
// These are not OK
XCTAssertNotNil(anObject, failureDescription);
XCTAssertNotNil(anObject, formatSpecifier, failureDescription);

最后但同样重要的是,正如前面已经提到的,对 XCTest 框架的引用需要添加到单元测试目标中。如果您收到链接器错误,例如Undefined symbols for architecture i386: "_OBJC_CLASS_$_XCTestCase", referenced from: foo.

Xcode 6 更新:在 Xcode 6 中不再需要链接到 XCTest(实际上 XCTest 甚至不再被列为可用框架)。而是将构建设置 CLANG_ENABLE_MODULES 设置为 YES(在 UI 中显示为“启用模块(C 和 Objective-C)”)。这将导致clang在看到语句时自动链接到 XCTest #import <XCTest/XCTest.h>。详细信息可在clang 文档的“模块”部分中找到。


第 2 部分:如何在 Xcode 5 中运行 OCUnit 测试

在这一点上,我遇到了一个链接器错误,这让我意识到我迁移到 XCTest 的任务失败了。原因:XCTest 不是 SDK 6.1 的一部分,但我仍在使用基本 SDK iOS 6.1 构建我的项目(这个 SO 答案解释了如何将 SDK 6.1 集成到 Xcode 5 中)。

由于我无法继续迁移,因此我目前的解决方案是保留基于 SenTestingKit/OCUnit 的单元测试,直到我有时间将我的应用程序升级到 iOS 7。这是我必须做的事情让单元测试运行:

  1. 从单元测试目标中删除“运行脚本”构建阶段。这就是让 Xcode 在选择单元测试目标时通过“测试”操作 ( + U)执行单元测试所需的全部内容。
  2. 但是,这并不理想,因为我不想仅仅为了执行单元测试而切换目标。相反,我想在选择主要目标时执行单元测试。因此,第二步是修改主目标的 Xcode 方案,以便在我运行“测试”操作时,执行单元测试目标的测试。

最终的解决方案不如 Xcode 4.x 中的好,在 Xcode 4.x 中,每次我运行主要目标的“运行”或“构建”操作时都会自动执行单元测试。不幸的是,如果没有“运行脚本”构建阶段,我似乎无法让它工作。

于 2013-11-17T04:33:15.173 回答
14

Edit -> Refactor -> Convert to XCTest

OCUnit tests will still work, but you might as well migrate. The changes end up being quite minimal.

于 2013-11-16T19:09:15.577 回答