使用Swift协议提高代码可测试性
作为开发人员,我们最大的挑战之一是实现高代码可测试性。 这些测试对于确保您开发的代码可以正常工作,并且在添加新功能时没有任何损坏方面非常有用。 另外,当您在团队中工作时,会有很多人在修改项目。 因此,确保代码的完整性也很重要。
有很多测试,但是它们不应该是有问题的或复杂的。 那么,为什么没有很多开发人员这样做呢? 主要的原因是时间不足。 我相信,最大的问题之一是我们的代码在层,类和具有外部框架的依赖关系之间耦合太深。
我想证明,创建框架的抽象层或将类解耦不是一件容易的事。
想象一下,我们需要开发一个应用程序,该应用程序需要知道用户的位置,因此,我们需要使用CoreLocation 。
我们的ViewController可能看起来像这样:
它有一个locationManager作为CLLocationManager ,以请求用户的位置或请求授权(如果适用)。 它还符合CLLocationManagerDelegate协议,并在其中接收locationManager输出。
在这里,我们可以看到ViewController与CoreLocation以及其他与职责分离相关的问题结合在一起。
无论如何,让我们为ViewController创建测试。 这可能是一个很好的例子:
我们可以看到sut ( 被测系统)和可能的测试之一。 在这里,我们请求用户的位置,并将其存储在我们的本地变量( userLocation )中。
这里的问题开始出现…… CLLocationManager管理请求,它不是一个同步过程,因此当我们检查存储的位置仍然为nil时 。 同样,我们可能没有授权请求位置的权限,在这种情况下,位置也将为nil 。
现在,我们有一些可能的解决方案! 让我们在不测试与位置相关的任何东西的情况下测试ViewController ,创建CLLocationManager的子类并模拟方法,或者尝试正确地进行操作并将CLLocationManager与我们的类分离。 我选了最后一个
“ Swift设计的核心是两个令人难以置信的强大想法:面向协议的编程和一流的值语义” – Apple
POP是开发人员的强大工具,而Swift无疑是一种面向协议的语言。 所以我的建议是使用协议解决这些依赖性。
首先,要抽象CLLocation ,我们将定义仅包含代码所需变量或函数的协议。
现在,我们可以获得一个没有CoreLocation的位置。 因此,如果我们分析ViewController ,我们可以看到我们实际上并不需要CLLocationManager ,而只有在我们请求时向我们提供用户位置的人才可以。 因此,我们将创建一个包含我们需求的协议,只要符合该协议的人都可以作为提供者。
在我们的例子中,我们创建了UserLocationProvider。 该协议指定我们只需要一个方法来请求用户的位置,结果将通过我们提供的回调。
我们准备创建一个UserLocationService ,它符合该协议并向我们提供位置。 通过这种方式,我们已经在类中解决了CoreLocation的依赖关系,但是请稍候…… UserLocationService需要通过CLLocationManager请求位置……因此,看来问题尚未解决😅
再次进行救援的协议,只需创建一个新协议即可为我们指定什么是位置提供者:
我们扩展了CLLocationManager功能,以符合我们的新协议。
现在是的,我们准备创建UserLocationService🎉 ,它看起来像这样:
UserLocationService有他的位置提供者,但是他不知道自己是谁,对他来说没关系,他只在需要时才需要用户的位置,其余的都不由他负责。
需要扩展以符合CLLocationManagerDelegate协议,因为我们将使用CoreLocation。 但是我们在测试中将如何看待,我们实际上并不需要它来验证我们的类工作正常。
我们可以在协议中添加任何类型的委托,但是对于这个示例来说,太多了,我认为
在开始测试之前,我们先来看一下使用UserLocationProvider而不是CLLocationManager的 ViewController的外观。
查看这段代码,我们得出的结论是,现在,我们的ViewController具有更少的代码,更少的责任和更多的可测试性。
让我们进行测试。 首先,我们将创建一些需要测试ViewController的模拟类。
使用这些模拟,我们可以注入所需的任何结果,我们将模拟UserLocationProvider的工作方式。 因此,我们将焦点放在真正的目标ViewController上 。
我们创建了两个测试,一个测试检查我们是否无权请求该位置,提供者不提供任何内容。 而另一种情况则相反,如果我们被授权,我们应该获得用户的位置。 如您所见,测试已通过!! 💪
除了ViewController之外 ,我们还创建了一个附加类UserLocationService ,因此我们也应该介绍他。
需要模拟LocationProvider ,因为它不是此测试的目标。
可以创建许多测试,但是请验证提供者是否说我们有授权(如果没有要求,否则我们要求位置)可以是其中之一。
您可以想象,有许多方法可以使代码解耦,而本出版物只是其中之一。 但是我认为这可能是一个很好的例子,表明测试并不是一项艰巨的任务。
如果您记得顶部的图像,您会看到乐高积木,这就是为什么我认为它们很好地解释了什么是解耦和抽象您的组件。 最后,它被定义为一种特定的连接方式,但是颜色并不重要。
也许最懒的任务之一就是创建模拟,但是为此,已经有库和工具来简化这项工作,例如Sourcery 。 另外,这是我的同事Hugo Peral的文章,解释了如何使用Sourcery节省测试时间。 或是John Sundell撰写的这篇文章,它提供了有关如何制作模拟游戏的更多细节。
没什么,感谢您阅读本出版物。 如果对您有用,或者您认为对某人有用,请分享it。 如果您有任何疑问或任何改进建议,请在下面发表评论。
- Xcode多个静态库和重复的符号
- Linkedin – iOS – 检测用户取消login
- 传递两个零参数initWithNibName:bundle:方法不好的做法(即不安全或较慢)?
- 将contentMode设置为UIViewContentModeScaleAspectFit时,如何设置UIImageView左alignment或右alignment?
- 我有videourl,并希望使用此URl从iPhone中删除它
- 自动从iTunes Connect下载销售报告
- 如何在同一个目标中有效地使用Autolayut和自动resize?
- Facebooklogin错误“用户不被允许查看应用程序”。
- 使用telprompt的应用程序是否会被拒绝?