17

因此,当我开始对简单的 2 行postNotificationand进行单元测试时,我的这个问题就开始了addObserver从这里的类似问题中,您可以看到要使其可测试,您需要添加约 20 行代码,并编写代码的常用方式分开。

面对这个问题,其实是我第一次明白单元测试和TDD的区别。如果您的代码是可测试的,即如果您遵循 TDD 思维方式,则单元测试很容易。接下来我被带到了如何编写可测试的代码,我没有找到太多的指导方针,每个教程都只是跳到编写单元测试。苹果自己的文档对此一无所知。

我最初的想法是,我需要以“函数式编程”为目标,并以纯函数的方式编写函数。但话又说回来,这非常耗时,可能需要对现有代码进行大量重构,甚至对于新项目也需要大量添加行,我什至不确定这是否是正确的方法。是否有任何建议的指南或标准以简单的方式编写可测试的代码?

我自己已经知道的:我知道你不应该写任何代码,除非有一个测试让它失败,所以基本上我必须先写测试,一旦我得到一个错误,甚至是编译器错误然后我将不得不切换回正在测试的实际类,编写任何必要的内容并使我的测试代码不给出任何错误,然后切换回测试类并继续编写我的测试并修复编译错误,直到完成。然后运行测试,看看它是否检查了我想要检查的内容。

对于所有测试,我应该确保我的测试会在我预期失败的地方通过和失败,即测试会在预期失败时通过。

我不知道如何以更简单的方式使流程变得顺畅。

我不是在询问如何为 NSNotificationCenter 编写可测试的代码,我是在询问编写可测试代码的一般准则。

4

1 回答 1

20

这是一个相当大的问题,并且开发人员的观点会转向非常不同的方向。同样重要的是要注意,使代码可测试在许多基本方面不是 Swift 特定的问题:使您能够常规且方便地编写可测试代码的许多原因实际上依赖于您遵循一些基本的、普遍适用的原则。测试驱动的设计实践通常会间接地帮助您,通过验证您是否遵循了通过测试执行代码的实践,除了为您带来其他程序员的生产力和可靠性优势之外。因此,不幸的是,编写可测试的代码并不是学习使用 Xcode 的一些机械技巧的问题,而不是通常证明您已经设计和计划了您编写的程序和库以遵循一些良好实践的问题。

我会尽力在下面链接到一些 Swift 特定资源,以展示我倾向于遵循的更一般的原则,以使我的代码可测试。

  1. 使您的代码可测试通常是遵循健全的面向对象设计原则的副作用。

    • 您可能会发现阅读SOLID 原则很有用。
    • 还可以查看这个基于 Swift的五个 SOLID 原则中的每一个的演示。
    • Google 的 Code Reviewer's Guide ( PDF ) 也是了解典型 OOD 问题以及如何避免的重要资源(也并非巧合,此 OOD 相关文档的副标题恰好是“编写可测试代码”)。
  2. 遵循良好的面向对象设计本身通常是良好的更高层次架构决策的副作用。基本上,尽早并经常考虑您计划在程序的对象图中引入的类型。它们之间的作用和依赖关系是什么?从不同的上下文(例如,从 UI 代码与单元测试)执行代码时,对象图中的任何依赖项是否难以满足或正确构建?

    • 有很多关于建筑设计模式的计算机科学文献。《四人帮》仍然是一本值得阅读的关于这个主题的书(尽管并非所有内容都适用于你的典型 Swift 程序)。
    • 查看这个Swift 设计模式演示,了解可以在 Swift 中实现多少种常见设计模式。
    • 尤其是如果您的代码是针对移动应用程序的,您应该阅读 VIPER,这是一种面向移动应用程序的架构模式,它源于 iOS 应用程序的典型架构需求。
    • 要将设计模式与上面列出的 SOLID 原则联系起来,“单一责任”原则可能是许多大型 Cocoa 程序中最明显违反的一项原则,这是由于糟糕的架构实践导致的,也导致代码测试非常困难。事实上,人们经常开玩笑地将在 Cocoa 中应用的 MVC 称为“Massive View Controller”

上面提出的观点绝不是 Swift 特有的,我希望不会有非常有争议的说法。然而,Swift 也提供了语言特性,这些特性也可以帮助您编写可靠、无错误的代码,部分原因是这些语言特性可以帮助您使代码测试变得友好。以下是一些好的 Swift 实践:

  • 尽可能使用值类型:Swift 非常适合编写最小化自动引用计数引用(指针)的程序。这带来了性能优势,但也提高了编写可靠代码的机会,从而最大限度地减少在测试中难以捕获的意外依赖和数据竞争。
  • 尽可能保持不可变:Swift 中的语法约定和类型系统可帮助您避免可变状态,其中包括对代码的可测试性产生负面影响(配置对象图以进行测试可能变得困难,实现真实-如果您的程序中可能的状态空间很大,则您的代码的世界可用测试覆盖率)。
  • Apple 还提供了一些关于值类型和不变性的架构问题的指南,这些指南也涉及代码的可测试性。
  • 尽可能使用协议:要了解原因,请阅读Liskov 替换原则:-) 更严重的是,优先选择协议而不是具体类型可以帮助您编写可测试的代码,例如允许您在测试设置中使用特定类型的测试来满足依赖关系伪造或模拟一些与您的测试无关的资源。
  • 使用函数式编程技术(在有意义的情况下):通常将功能与函数组合起来有助于您编写可避免可变状态的可读、灵活的代码。我推荐Chris Eidhof 和其他人的 Functional Swift作为在 Swift 中应用函数模式的一个很好的指南。

再一次,这个问题很大,我的回答只是触及表面,为了表明可测试性没有单一的灵丹妙药——这是您在设计代码时遵循许多最佳实践的结果.

于 2016-10-16T23:41:42.757 回答