code + S的Xcode单元测试

使用Xcode IDE进行TDD可能会造成破坏。 每次在实现或测试文件中进行小的更改时,Xcode都会重建整个项目(幸运的是使用增量构建),并将该应用程序安装在模拟器上。 根据您拥有的项目配置,可能需要几秒钟到几十秒的时间。 本文提供了一种方法,可以在您保存实施.swift文件时绕过该持久过程并自动运行单元测试。 我们将利用John Holdsworth的“ Injection for Xcode”应用程序的新功能-支持自动测试。

TLDR;
InjectionTDD(与InjectionForXcode一起)在模拟器环境上运行单元测试,而无需重建或重新安装应用程序。 实施和测试已注入到已经运行的主机应用程序中,因此几乎可以在标准的知名Xcode IDE中实时运行。 另外,为了加快测试过程,InjectionTDD仅运行一部分测试用例,这些用例与刚更新的实现文件有关。

如果您正在进行TDD测试,则要获得快速反馈,无论测试是通过(绿色)还是失败(红色),这一点至关重要,但不仅如此。 立刻知道您的更改/修复不会在其他地方引入任何回归会不会很好? 还是您可能要用新的测试用例来覆盖实现,而重新构建测试目标就变得很沮丧? 如果您发现相同的问题,InjectionTDD可以节省大量时间!

通常,要运行单元测试,您必须等待几个步骤,例如:构建,链接,安装整个应用程序以及将Xcode与lldb服务器连接。 根据您的配置,最多可能需要数十秒钟。 但是,在开发过程中,我们经常只修改一个特定文件,然后观察结果。 InjectionForXcode通过实时编译更新的文件并在实时iOS流程中替换/替换其实现方式来节省大量时间。 非常相似的方法可以应用于单元测试。 通常,这就是InjectionForXcode + InjectionTDD工作方式:

首先,Xcode构建您的测试目标,在模拟器上安装应用程序,然后尝试执行第一个测试。 但是,必须将专用框架称为InjectionTDD集成到测试目标中,使测试执行停止,并使运行时保持恒定的等待循环。

然后,每当您在Xcode中进行某些更改时,InjectionForXcode都会构建应用程序的一部分(仅编辑文件+相关的单元测试),并将其注入已经等待的“托管应用程序”中。 最后,主机应用程序执行所有测试,并将结果传递回Xcode,以将其呈现在其标准UI中。 请记住,主机应用程序此后不会终止,可以重复整个过程。

拥有理论背景,让我们卷起袖子,玩转它。 您可以使用以Swift编写的带有单元测试的任何现有项目开始,也可以通过pods命令pod try InjectionTDD

  • 在进行任何工作之前,请确保终端命令xcode-select -p指向当前的Xcode版本路径。 如果不是,请使用-s选项分配正确的版本,例如:
    sudo xcode-select -s /Applications/YOUR_XCODE.app/Contents/Developer
  • 从官方站点安装免费的“ Injection for Xcode”应用程序(请记住安装具有TDD支持的版本)并运行它。 请注意 ,应用程序仅位于纸盘中。
  • InjectionTDD框架集成到您的测试目标(支持CocoaPods,Carthage和手动安装)。 如果您使用CocoaPods,则只需一行更改:
 目标“ YourAppTests” 
pod'InjectionTDD','〜> 0.5'
结束
  • 在模拟器上启动单元测试(⌘+ U) 稍后,Xcode控制台应打印确认Connected to “Injection” plugin, ready to load x86_64 code
  • 我们准备编辑并保存您的任何实现.swift文件,然后单击“注入”菜单托盘中的“注入源”选项(或使用快捷键⌃ =)。

InjectionForXcode开始发挥作用,一段时间后,您应该在控制台输出和“测试导航器”(⌘+ 6)中看到测试结果。

  • (可选) :您可以在任务栏菜单中启用“ File Watcher”,以便在保存文件时自动进行注入。 这真的很方便。
  • 可选 :默认情况下,测试结果会打印到控制台,但是如果您还希望显示通知摘要(如下所示),请安装自定义Xcode断点以实现相同的结果(请参见手册):

如果您对实现细节感兴趣,则每当对实现文件进行更改时,就会发生四个阶段:

  1. 查找所有测试目标文件以查找直接从实现文件中调用代码的测试用例:使用Swift的元数据文件.swiftdeps ((. .swiftdeps描述给定文件提供和依赖的Swift符号),
  2. 使用增量构建(从Xcode日志中解析命令)来编译您的swift项目目标,并将其捆绑到.framework
  3. 将刚编译的二进制文件发送到模拟器进程:iOS模拟器应用程序从磁盘动态框架加载,
  4. 在模拟器上执行所有XCTestCase

与依赖于消息调度的Objective-C相反,Swift也使用静态调度,这使得运行时方法无法执行。 但是,对于InjectionTDD而言,情况并非如此,实现和单元测试在链接过程中绑定在一起,因此单元测试始终指向最新的实现。 换句话说, XCTestCase与正在测试的唯一类型紧密耦合(在编译时)。

因此,您可以使用所有调度机制测试各种Swift类型: classes, final classesstructs, enums等。

默认情况下, InjectionTDD框架停止所有测试的执行,并等待注入的测试。 这是在开发期间完成的,但是在CI机器上,我们希望正常执行所有测试,并且从不希望进行任何注入的测试。 如果您担心仅在本地集成InjectionTDD框架是过大的选择,那么可以采取一种补救措施。 您可以通过称为INJECTION_TDD_SKIP的方案环境变量中的特殊标志来控制测试执行(正常运行还是等待注入)。 如果将其设置为TRUE,则所有单元测试将照常执行:

因此,建议至少保留两个用于回购的方案:

  1. 用于建筑,其中INJECTION_TDD_SKIP=TRUE
  2. 仅TDD开发(禁用了INJECTION_TDD_SKIP env。)

您的CI构建脚本始终使用前一种方案,开发人员可以在编码时切换到后一种方案。

提示:为了加快整个注入过程,开发TDD方案(2.)不应收集Code的覆盖范围。 它可以切断宝贵的几秒钟。

“ Injection for Xcode”是一个功能强大且流行的工具,用于在运行时中实时更新应用程序,尤其是在掌握UI详细信息时。 如今,它还支持实时的单元测试,从而节省了大量时间,并与Xcode IDE集成在一起。

安装非常简单:下载Injection app,将CocoaPods或Carthage库集成到您的测试目标,并为获得出色的TDD体验做好准备!