iOS单元测试

基本上,单元测试的思想非常简单:

了解输入并将输出与期望值进行比较,我们可以验证黑盒是否正常工作。

  + ---------------------- + 
输入| | 输出量
+ ----------> +黑匣子+ ------------>
| |
+ ---------------------- +

尽管经常知道单元在您的项目中实现了什么功能,但是对于单元测试环境仍然未知,这就是为什么在这里使用Blackbox术语的原因。

但是什么是单元测试单元? 嗯,有很多定义,但是它们都以一种或另一种方式将Unit定义为功能单元,我想说是不可分割的功能单元

在Swift语言中,功能的不可分割的单元是一个功能。 每个函数都可以带有一些参数(输入),可以返回某个值(输出),因此可以通过单元测试来验证其功能。

您可以编写一个涉及整个类型(类或结构)或什至组合成一个模块的几种类型的单元测试,但是:

一个单元测试一个功能绑定在一起可以使您的测试尽可能地适应代码。

好的,让我们玩一下Swift编程吧。 因此,当您具有如下方法或函数时,单元测试的想法非常简单:

为了验证它是否正确运行,您编写了一些传递已知Params代码,并检查返回的Result是否与这些参数所期望的一样。 这个特殊的代码称为单元测试,因为它测试一个单元doSomeWork函数。

这是一个精致的示例,在现实世界中, doSomeWork函数而不是Class或Structure的方法,因此其工作可能取决于其他方法和属性。 尽管通常依赖另一种方法并不危险,但是应注意依赖属性和实例变量,因为它们描述了类型的状态并且在测试之间是持久的,此处的规则:

使您的方法成为函数式编程的纯函数

纯函数是不依赖所有者状态且不会产生副作用(更改所有者状态)的函数。

像许多其他现代语言一样,Swift也是面向对象的,它具有具有属性和实例变量的类,这使得不可能将所有方法都当作纯函数使用,特别是如果它是UI应用程序。 万一该方法无法重构为纯函数,您的规则是:

尽可能减少方法对其所有者状态的依赖

使用纯函数(除了使代码可测试以外)的另一个有用效果是,它们将函数式编程的优点引入了您的应用程序。 现在可以安全地构建方法链,并且对于相同的输入参数,其结果将始终相同。 它还通过增加可扩展性和减少耦合来改善应用程序体系结构。

回到无法摆脱对状态的依赖的方法,可以依靠的一种提高其可测试性的技术是依赖注入 考虑下一节课:

在这里, Jobs将在doSomeWork方法中执行实际工作的Jobs委托给ConcreteWorker ,由于以下原因,我们不能删除使doSomeWork不利于测试的依赖关系:

  • ConcreteWorker可能依赖于在测试过程中难以设置或无法设置的环境,例如,它可能会使用Simulator无法使用的iOS功能
  • Jobs可以开始使用ConcreteWorker的子类来覆盖work(:)方法,或者甚至可以在运行时选择适当的工作器

这些是以下事实的结果: Jobs控制了ConcreteWorker实例,而无法从外部进行管理。 为了解决这个问题,我们将使用依赖注入来反转此控件:

请注意,我们还引入了Worker协议,该协议为Jobs使用的工人定义了一个合同,该合同将其与具体的worker实施分离。

单元测试还有另一条规则:

将测试测试的类隔离

这意味着要测试doSomeWork方法,我们只需要检查是否用指定的参数调用了Worker的方法work ,我们就不需要验证Worker实际工作,因为对此应该进行单独的测试。

经过最新的更改之后,现在编写测试非常简单:

这是TestWorker的虚假实现,我们只是为此测试创建的,它在单元测试中称为模拟对象。

最后,这不是真正的规则,而更像是代码气味:

如果很难为该方法编写测试,则该方法可能有问题

让您的架构成为控制简单事物的智能系统!