因此,当我开始对postNotification
和addObserver
的简单2行进行单元测试时,我的这个问题就开始了。从这个类似的问题中,你可以看到,为了使其可测试,你需要添加大约20行&远离编写代码的常用方法。
面对这个问题实际上是我第一次理解单元测试和TDD之间的区别。如果你的代码是可测试的,即如果你遵循TDD的思维方式,那么单元测试是很容易的。接下来,我被带到了如何编写可测试的代码,我没有找到太多的指导方针,每个教程都只是跳到编写单元测试中。苹果公司自己的文档对此一无所知。
我最初的想法是,我需要以"函数编程"为目标,用纯函数的方式编写函数。但话说回来,这是非常耗时的,可能需要对现有代码进行大量重构,甚至对于新项目也需要添加大量行,我甚至不确定这是否是正确的方法。有没有建议用简单的方式编写可测试代码的指导方针或标准?
我已经知道自己:我知道你不应该写任何代码,除非有测试失败,所以基本上我必须先写测试,一旦我出现错误,甚至是编译器错误,我就必须切换回被测试的实际类,写任何必要的东西,让我的测试代码不出现任何错误,然后切换回测试类,继续编写测试并修复编译错误,直到完成为止。然后运行测试,看看它是否检查了我希望它检查的内容。
对于所有的测试,我都应该确保我的测试在我预期失败的地方通过和失败,即测试在预期失败的时候通过。
我不知道的是,我如何才能以一种更容易的方式使这个过程变得顺利。
我不是在问如何为NSNotificationCenter编写可测试代码,我是在问编写可测试的代码的一般指南
这是一个相当大的问题,开发人员的观点会朝着非常不同的方向发展。同样重要的是要注意,让代码可测试在很多方面都不是Swift特有的问题:让你能够经常方便地编写可测试代码的很多因素实际上取决于你遵循一些基本的、普遍适用的原则。通常,测试驱动的设计实践会间接地帮助您,通过验证您是否遵循了使通过测试执行代码变得合理的实践,以及为您带来其他程序员生产力和可靠性方面的好处。因此,不幸的是,编写可测试代码并不是学习使用Xcode的一些机械技巧的问题,而是证明你已经按照一些良好的实践设计和规划了你编写的程序和库。
我将尽我所能在下面链接到一些Swift特定的资源,以展示我自己为使代码可测试而遵循的更一般的原则。
-
使代码可测试通常是遵循健全的面向对象设计原则的副作用。
- 你可能会发现阅读关于SOLID原则的文章很有用
- 还可以看看这个基于Swift的五个SOLID原则中的每一个的演示
- 谷歌的代码评审员指南(PDF)也是学习典型OOD问题和如何避免OOD问题的绝佳资源(同样并非巧合的是,这份OOD相关文档的副标题恰好是"编写可测试代码")
-
遵循一个合理的面向对象的设计本身往往是良好的更高级别架构决策的副作用。基本上,尽早且经常地考虑您计划在程序的对象图中引入的类型。它们之间的角色和依赖关系是什么?当从不同的上下文执行代码时(例如,从UI代码到单元测试),对象图中的任何依赖项是否很难满足或正确构建?
- 有很多关于建筑设计模式的计算机科学文献。关于这个话题,《四人帮》仍然是一本很有价值的书(尽管并不是所有的书都适用于典型的Swift程序)
- 看看这个Swift设计模式演示,了解Swift中可以实现多少常见的设计模式
- 特别是如果你的代码是为移动应用程序编写的,你应该阅读VIPER,这是一种基于iOS应用程序典型架构需求的面向移动应用程序的架构模式
- 为了将设计模式与上面列出的SOLID原则联系起来,"单一责任"原则可能是许多大型Cocoa程序中最明显违反的原则之一,这是由于糟糕的体系结构实践造成的,也导致了非常难以测试代码。事实上,人们经常开玩笑地把在Cocoa中实际应用的MVC称为"海量视图控制器">
上述观点与Swift无关,我预计不会有太大争议。然而,Swift也提供了一些语言功能,这些功能也可以帮助您编写可靠、无错误的代码,部分原因是这些语言功能可以帮助您使代码测试变得友好。以下是一些良好的Swift实践:
- 尽可能使用值类型:Swift非常适合编写尽量减少自动引用计数引用(指针)的程序。这带来了性能优势,但也提高了编写可靠代码的机会,最大限度地减少了在测试中难以捕获的意外依赖性和数据竞争
- 尽可能保持不变:Swift中的语法约定和类型系统有助于避免可变状态,这对代码的可测试性有负面影响(如果程序中可能的状态空间很大,那么配置对象图进行测试可能会变得困难,实现代码的实际可用测试覆盖率也会变得困难)
- 苹果还提供了一些关于值类型和不变性的体系结构问题的指导,这些指导也涉及到代码的可测试性
- 尽可能使用协议:要理解原因,请阅读Liskov替代原则:-)更重要的是,更喜欢协议而不是具体类型有助于编写可测试代码,例如,允许您在测试设置中使用特定于测试的类型来实现依赖关系,这些类型会伪造或模拟一些与测试无关的资源
- 使用函数式编程技术(如果有意义的话):经常用函数组合功能可以帮助您编写可读、灵活的代码,避免可变状态。我推荐Chris Eidhof等人的Functional Swift作为在Swift中应用功能模式的好指南
同样,这个问题很大,我的答案只是触及了表面,目的是表明可测试性没有单一的灵丹妙药——这是在设计代码时遵循许多最佳实践的结果。