我该如何从SenTestingKit/OCUnit迁移到XCTest?

35

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

RunUnitTests已过时。

可能与此相关的是Xcode 5发布说明中的这个注释:

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

不幸的是,我还没有能找到有关这个神秘的“迁移器”的更多信息。可能我的搜索技巧不够好,所以我的主要问题是:如何将基于SenTestingKit/OCUnit的单元测试迁移到新的XCTest(无论是否使用“迁移器”)?

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


如果你像我一样疯狂到想要在iOS7上运行XCTest和在iOS6上运行OCUnit,可以查看我的博客文章:http://blog.7actionsdev.com/2014/04/using-ocunit-for-ios6-and-xctest-for.html。 - phatmann
2个回答

59

多亏了Shaggy Frog的回答,我们现在知道Xcode发布说明中提到的神秘的“迁移器”是通过选择“编辑 > 重构 > 转换为XCTest”启动的向导。我将分两部分写关于这个向导的经验。第一部分是关于主要问题的不完整答案,第二部分回答了次要问题。


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

首先你需要注意,为了让向导工作,你需要选择一个单元测试目标。如果你选择的是主目标,向导将简单地不列出任何要转换的目标。

当我发现这一点时,我能够使用向导步骤,但在我的情况下,最终结果仍然是一个惊人的失败!向导声称不需要进行任何源代码更改,只需更新构建设置即可迁移到XCTest。最终,向导甚至未能正确完成此操作:确实删除了对SenTestingKit框架的引用,但没有放入对XCTest框架的引用。

无论如何,接下来是我必须手动进行的更改列表,因为向导未能为我做出这些更改。如果向导对你有效,则可能不需要执行所有这些操作。

  1. 从单元测试目标中删除“运行脚本”构建阶段
  2. 将所有测试用例类的基类从SenTestCase更改为XCTestCase
  3. 将导入的头文件从<SenTestingKit/SenTestingKit.h>更改为<XCTest/XCTest.h>
  4. 在测试目标的构建设置中,将包装器扩展名从octest更改为xctest
  5. 将所有断言宏从ST*重命名为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在看到#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。以下是我要执行的操作才能使单元测试运行:

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

最终的解决方案不如在Xcode 4.x中好,那里每次运行主目标的“Run”或“Build”操作时都会自动执行单元测试。不幸的是,似乎我不能在没有“Run Script”构建阶段的情况下实现此目标。


1
在我的项目中,似乎所有的东西都已经转换为XCTest并且运行良好,但我仍然收到OCUnit警告。当我再次执行自动转换(没有代码更改)时,警告消失了,但下次打开XCode时它仍然存在。有什么帮助吗? - Ilya K.
这是一个小问题,我想也许这个帖子中的某个人遇到了这个问题。但可能你是对的,我应该开一个单独的帖子。 - Ilya K.
我刚刚添加了一步,将构建设置中的Wrapper Extension更改为“xctest”。向导通常会自动完成此操作。 - phatmann
现在Xcode 6已经发布,我请求更新此答案以确认所有内容仍然适用。同时,请求提供任何适用于针对Mac应用程序的信息。 - William Entriken
@herzbube 感谢你迄今为止的工作,很抱歉听起来像是在指派任务给你。再试一次。大家好,XCode 6 的用户们,你们能确认这还是有效的吗? - William Entriken
显示剩余4条评论

14

编辑 -> 重构 -> 转换为 XCTest

OCUnit 测试仍然可以工作,但最好迁移。更改最终会相当微小。


啊哈,隐藏在重构下面……好吧,情况可能会更糟。无论如何,第二个向导屏幕应该让我选择要转换的目标。在我的情况下,列表是空的。有什么想法为什么? - herzbube
我现在确定这一定与我的单元测试目标的设置方式有关。我尝试添加了一个新的空单元测试目标(使用文件>新建>目标),那个目标在向导中显示得非常好。我将继续调查,并在找到解决方案后发布结果。 - herzbube
2
xCode 6现在位于菜单“编辑->转换->到XCTest...”下。 - ajmccall

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接