感谢 Shaggy Frog 的回答,我们知道 Xcode 发行说明中提到的神秘“迁移器”是通过选择“编辑 > 重构 > 转换为 XCTest”启动的向导。我将分两部分写下我对这个向导的体验。第一部分是对主要问题的不完整回答,第二部分回答次要问题。
第 1 部分:如何从 OCUnit 迁移到 XCTest
您需要意识到的第一件事是,要使向导工作,您需要选择一个单元测试目标。如果您选择了主要目标,则向导不会列出任何要转换的目标。
一旦我发现了这一点,我就能够逐步完成向导,但就我而言,最终结果仍然是一个惊人的失败!该向导声称不需要更改源代码,只需更新构建设置即可迁移到 XCTest。最后,向导甚至没有正确地做到这一点:它确实删除了对 SenTestingKit 框架的引用,但它没有添加对 XCTest 框架的引用。
无论如何,以下是我必须手动进行的更改列表,因为向导未能为我进行更改。如果向导更适合您,您可能不需要执行所有这些操作。
- 从单元测试目标中删除“运行脚本”构建阶段
- 将所有测试用例类的基类从
SenTestCase
更改为XCTestCase
- 将导入的标头从更改
<SenTestingKit/SenTestingKit.h>
为<XCTest/XCTest.h>
- 在测试目标的 Build Settings 中,将 Wrapper Extension 从 更改
octest
为xctest
.
- 将所有断言宏从
ST*
to重命名XCT*
(例如STAssertTrue
变成XCTAssertTrue
)
- 上述例外:
STAssertEquals
需要重命名为XCTAssertEqual
(注意末尾缺少的“s”)。如果您收到此编译器警告,您将知道您已经忘记了这一点:warning: implicit declaration of function 'XCTAssertEquals' is invalid in C99
- 新的 XCTest 断言宏不允许
nil
作为故障描述传递。例如,XCTAssertNotNil(anObject, nil)
不可能,必须更改为XCTAssertNotNil(anObject)
. 当你得到这个编译器错误时,你就会知道你有这个问题:error: called object type 'NSString *' is not a function or function pointer
.
- 如果您确实需要传递失败描述,新的 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。这是我必须做的事情让单元测试运行:
- 从单元测试目标中删除“运行脚本”构建阶段。这就是让 Xcode 在选择单元测试目标时⌘通过“测试”操作 ( + U)执行单元测试所需的全部内容。
- 但是,这并不理想,因为我不想仅仅为了执行单元测试而切换目标。相反,我想在选择主要目标时执行单元测试。因此,第二步是修改主目标的 Xcode 方案,以便在我运行“测试”操作时,执行单元测试目标的测试。
最终的解决方案不如 Xcode 4.x 中的好,在 Xcode 4.x 中,每次我运行主要目标的“运行”或“构建”操作时都会自动执行单元测试。不幸的是,如果没有“运行脚本”构建阶段,我似乎无法让它工作。