多亏了Shaggy Frog的回答,我们现在知道Xcode发布说明中提到的神秘的“迁移器”是通过选择“编辑 > 重构 > 转换为XCTest”启动的向导。我将分两部分写关于这个向导的经验。第一部分是关于主要问题的不完整答案,第二部分回答了次要问题。
第一部分:如何从OCUnit迁移到XCTest
首先你需要注意,为了让向导工作,你需要选择一个单元测试目标。如果你选择的是主目标,向导将简单地不列出任何要转换的目标。
当我发现这一点时,我能够使用向导步骤,但在我的情况下,最终结果仍然是一个惊人的失败!向导声称不需要进行任何源代码更改,只需更新构建设置即可迁移到XCTest。最终,向导甚至未能正确完成此操作:它确实删除了对SenTestingKit框架的引用,但没有放入对XCTest框架的引用。
无论如何,接下来是我必须手动进行的更改列表,因为向导未能为我做出这些更改。如果向导对你有效,则可能不需要执行所有这些操作。
- 从单元测试目标中删除“运行脚本”构建阶段
- 将所有测试用例类的基类从
SenTestCase
更改为XCTestCase
- 将导入的头文件从
<SenTestingKit/SenTestingKit.h>
更改为<XCTest/XCTest.h>
- 在测试目标的构建设置中,将包装器扩展名从
octest
更改为xctest
。
- 将所有断言宏从
ST*
重命名为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";
XCTAssertNotNil(anObject, @"foo")
XCTAssertNotNil(anObject, @"%@", failureDescription)
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
在看到#import <XCTest/XCTest.h>
语句时自动链接到XCTest。详细信息请参见clang文档的“模块”部分。
第二部分:如何在Xcode 5中运行OCUnit测试
此时,我遇到了链接器错误,意识到我迁移到XCTest的任务失败了。原因是:XCTest不属于SDK 6.1,但我仍在使用基础SDK iOS 6.1构建项目(这个SO答案解释了如何将SDK 6.1集成到Xcode 5中)。
由于我无法继续进行迁移,因此我目前的解决方案是保持我的单元测试基于SenTestingKit / OCUnit,直到我有时间将应用程序升级到iOS 7。以下是我要执行的操作才能使单元测试运行:
- 从单元测试目标中删除“Run Script”构建阶段。这是让Xcode通过“Test”操作(⌘ + U)执行单元测试所需的全部内容,而在选择单元测试目标时。
- 不过,这并不理想,因为我不想切换目标才能执行单元测试。相反,我希望在选择主目标时执行单元测试。因此,第二步是修改主目标的Xcode方案,以便在运行“Test”操作时执行单元测试目标的测试。
最终的解决方案不如在Xcode 4.x中好,那里每次运行主目标的“Run”或“Build”操作时都会自动执行单元测试。不幸的是,似乎我不能在没有“Run Script”构建阶段的情况下实现此目标。