如何使用FBSnapshotTestCase在iOS中进行UI测试
在视图控制器中更改某些代码已经发生了多少次,并且在生产环境中测试应用程序时,视图已经发生了神奇的变化? 如果我们不想,我们是否应该有某种方法来确保视图不变?
因此,进行UI测试很重要。 通过添加UI测试,我们可以确保不会对UI进行意外更改。
在本文中,我们将在一个简单的示例项目中专注于FBSnapshotTestCase的用法。
FBSnapshotTestCase的历史
最初,FBSnapshotTestCase由Facebook创建和维护。 这是一个非常有用的工具,具有非常简单的实现。 所需要做的就是将库集成到项目中,并实例化我们要测试的UIView
或UIViewController
子类。
不幸的是,自去年以来,Facebook已停止维护该工具。 Facebook已经创建了另一个与其内部基础设施紧密相关的工具,因此他们不赞成该工具。
对我们来说幸运的是, Uber决定为他们当前维护的项目创建一个分支。
FBSnapshotTestCase测试实际上是单元测试,它使我们能够将UIViewController
的参考图像(也可以是UIView,但通常是在全屏模式下)与视图的当前呈现进行比较。 如果存在任何不一致(第一张图像与第二张图像之间存在差异),FBSnapshotTestCase将生成第三张图像,并以灰度标出差异。 如果参考图像和项目中的电流之间存在差异,则该工具将生成此第三张图像,并且在该单元测试中会生成错误,从而导致测试套件产生故障。
Fastlane为我们提供了类似的工具Fastlane Snapshot(存储库),它使我们能够在Xcode UI测试中截取屏幕截图。 所有UI测试完成后,如果它们都获得批准,它将生成带有所有屏幕截图的HTML文件。 Fastlane Snapshot默认情况下还允许我们设置我们要查看的所有设备和语言。 这对于测试所有可能的组合非常有用(如果您有3种语言,应用程序中有10个屏幕,并且支持5种设备,则它会生成3 x 10 x 5 = 150个屏幕截图!)。 问题在于,这只是一个屏幕截图,它没有为我们带来有关图像是否具有正确UI的信息。
总而言之,FBSnapshotTestCase允许我们比较并告诉我们是否有任何错误,Fastlane Snapshot只是为我们带来了Xcode UI测试的屏幕截图。
我们将讨论以下主题,力图充分利用该工具的潜力:
- 如何集成FBSnapshotTestCase
- 使用SnapKit重构代码并使用FBSnapshotTestCase
- 编写脚本以生成具有不同分辨率的屏幕
要开始本教程,请下载以下示例项目https://github.com/fedejordan/FBSnapshotTestCaseExample
在模拟器中编译项目(我使用过iPhone 8),您将看到以下屏幕:
一个简单的View Controller,可以是任何应用程序的第一个屏幕,带有登录和注册按钮。
我已经使用CocoaPods集成了SDK。 您可以使用Carthage或您选择的包裹管理员。 集成该工具的步骤在存储库指南中。 为了方便起见,以下是使用CocoaPods的安装说明:
- 在终端应用程序中,我们输入
pod init
以创建podfile - 在podfile中,我们添加
pod 'FBSnapshotTestCase'
- 我们在终端中进行
pod install
以下载SDK
我在这里使用的版本是2.1.6
将SDK集成到项目中后,我们将打开CocoaPods生成的工作区并编译项目,以确保一切正常。
就像它在FBSnapshotTestCase官方指南中所说的那样,我们转到Edit Scheme -> Run configuration
,以正确设置参考和失败测试图像的文件夹(与参考图像不同的文件夹)。 我们设置相应的环境变量。
现在,我们可以使用FBSnapshotTestCase创建我们的第一个单元测试。 为此,我们必须执行以下操作:
- 我们创建一个新的单元测试,并子类
FBSnapshotTestCase
而不是XCTestCase
。 - 在测试内部,我们使用
FBSnapshotTestCase
告诉我们要截屏的视图。 稍后,我们使用UIStoryboard
类实例化ViewController
类。 - 在测试类内部,使用
setUp
方法中的self.recordMode = YES
运行测试。 这将在磁盘中创建参考映像。 每当我们想要在屏幕上进行有意更改时,我们都必须这样做。 - 删除启用记录模式的行,然后再次运行测试。
我们的代码应与此非常相似:
每当我们在UI中添加或修改内容时,都将使用此图像与项目生成的未来图像进行比较。
如果要查看测试结果,可以在我之前告诉过的同一存储库中对分支first_snapshot_test
。
让我们看一下该工具可以带给我们的实际用法。
假设我开始在一个团队中工作,并且我决定故事板不是可伸缩的,所以我决定将所有UI内容迁移到代码中。 我进行了一些调查,并且喜欢一个名为SnapKit的库。
我不会谈论如何使用该库的细节。 因此,我将直接显示使用故事板(现在使用SnapKit)在同一屏幕上执行的代码。
为此,需要删除故事板文件并从AppDelegate
实例化View Controller。 在测试中,我们使用let viewController = ViewController()
实例化类ViewController
。
打开FailureDiffs
文件夹,看看发生了什么:
似乎我们为图像视图约束设置了错误的值。 如我们所见,它是UI中唯一看起来不同的对象。
我们看一下代码并进行更改:
make.top.equalTo(titleLabel.snp.bottom).offset(100)
具有以下内容:
make.top.equalTo(titleLabel.snp.bottom).offset(60)
使用Cmd + U再次测试,单元测试应该可以。
您可以在示例git仓库中的snapkit_refactor
分支中使用SnapKit下载最终重构来进行checkout
。
在上一个示例中,我们看到了如何在iPhone 8上使用该工具。但是,如果我们想在支持的所有设备上进行测试,会发生什么?
我们必须随时配置测试以在不同的模拟器中进行测试。 不太实用的东西。
因此,我做了一个脚本,可以在我们想要的任何设备中进行测试。 与Fastlane Snapshot配置类似,但不支持语言。
要使用该脚本,需要在测试类的setUp()
方法中放置self.isDeviceAgnostic = true
以便将设备标识符(在这种情况下为设备分辨率)添加到文件名中。
该脚本是公共的,您可以在下面看到它。 您只需要更改workspace
和scheme
值:
它生成所有指定设备的所有屏幕截图的输出。 要查看任何屏幕截图中是否存在错误,我们必须查看其生成的输出。
在该示例中,我仅包括了iPhone设备,但它允许我们项目支持的任何其他设备。
稍后,我将尝试执行相同的脚本,但是增加了对语言的支持,生成了一个显示所有结果的网页。 与Fastlane快照的工作原理类似。
我们学习了如何使用简单但功能强大的工具,该工具使我们能够检查是否意外在UI中添加了任何不必要的更改。
我们看到了一个示例,该示例说明如何从情节提要中重构视图以使用SnapKit,以及FBSnapshotTestcase如何允许我们检查是否正确执行了操作。
我还共享了一个脚本,该脚本允许在任何指定的设备上创建屏幕截图。 我认为,当我们拥有在所有分辨率下需要特别注意的不同设计时,这很有用。
如果您喜欢这篇文章,或者看到可以解决的任何错误,请随时发表评论或发送电子邮件至fedejordan99@gmail.com
非常感谢你! 😀