Mocking的原则

难道 mocking 不是在作恶(evil)吗?

你可能听过 mocking 是在作恶。就像软件开发中的任何东西一样,它可以被用来作恶,就像 DRY(Don’t repeat yourself) 一样。
当人们 不听从他们的测试 并且 _不尊重重构阶段时_,他们通常会陷入糟糕的境地。
如果你的模拟代码变得很复杂,或者你需要模拟很多东西来测试一些东西,那么你应该 倾听 那种糟糕的感觉,并考虑你的代码。通常这是一个征兆:

  • 你正在进行的测试需要做太多的事情
    • 把模块分开就会减少测试内容
  • 它的依赖关系太细致
    • 考虑如何将这些依赖项合并到一个有意义的模块中
  • 你的测试过于关注实现细节
    • 最好测试预期的行为,而不是功能的实现

通常,在你的代码中有大量的 mocking 指向 _错误的抽象_。
人们在这里看到的是测试驱动开发的弱点,但它实际上是一种力量,通常情况下,糟糕的测试代码是糟糕设计的结果,而设计良好的代码很容易测试。

但是模拟和测试仍然让我举步维艰!

曾经遇到过这种情况吗?

  • 你想做一些重构
  • 为了做到这一点,你最终会改变很多测试
  • 你对测试驱动开发提出质疑,并在媒体上发表一篇文章,标题为「Mocking 是有害的」

这通常是您测试太多 实现细节 的标志。尽力克服这个问题,所以你的测试将测试 _有用的行为_,除非这个实现对于系统运行非常重要。
有时候很难知道到底要测试到 _什么级别_,但是这里有一些我试图遵循的思维过程和规则。

  • 重构的定义是代码更改,但行为保持不变。 如果您已经决定在理论上进行一些重构,那么你应该能够在没有任何测试更改的情况下进行提交。所以,在写测试的时候问问自己。
    • 我是在测试我想要的行为还是实现细节?
    • 如果我要重构这段代码,我需要对测试做很多修改吗?
  • 虽然 Go 允许你测试私有函数,但我将避免它作为私有函数与实现有关。
  • 我觉得如果一个测试 超过 3 个模拟,那么它就是警告 —— 是时候重新考虑设计。
  • 小心使用监视器。监视器让你看到你正在编写的算法的内部细节,这是非常有用的,但是这意味着你的测试代码和实现之间的耦合更紧密。如果你要监视这些细节,请确保你真的在乎这些细节。

和往常一样,软件开发中的规则并不是真正的规则,也有例外。Uncle Bob 的文章 「When to mock」 有一些很好的指南。

总结

更多关于测试驱动开发的方法

  • 当面对不太简单的例子,把问题分解成「简单的模块」。试着让你的工作软件尽快得到测试的支持,以避免掉进兔子洞(rabbit holes,意指未知的领域)和采取「最终测试(Big bang)」的方法。
  • 一旦你有一些正在工作的软件,小步迭代 应该是很容易的,直到你实现你所需要的软件。

Mocking

  • 没有对代码中重要的区域进行 mock 将会导致难以测试。在我们的例子中,我们不能测试我们的代码在每个打印之间暂停,但是还有无数其他的例子。调用一个 可能 失败的服务?想要在一个特定的状态测试您的系统?在不使用 mocking 的情况下测试这些场景是非常困难的。
  • 如果没有 mock,你可能需要设置数据库和其他第三方的东西来测试简单的业务规则。你可能会进行缓慢的测试,从而导致 缓慢的反馈循环
  • 当不得不启用一个数据库或者 webservice 去测试某个功能时,由于这种服务的不可靠性,你将会得到的是一个 脆弱的测试

一旦开发人员学会了 mocking,就很容易对系统的每一个方面进行过度测试,按照 它工作的方式 而不是 _它做了什么_。始终要注意 测试的价值,以及它们在将来的重构中会产生什么样的影响。
在这篇关于 mocking 的文章中,我们只提到了 监视器(Spies),他们是一种 mock。也有不同类型的 mocks。Uncle Bob 的一篇极易阅读的文章中解释了这些类型。在后面的章节中,我们将需要编写依赖于其他数据的代码,届时我们将展示 Stubs 行为。
作者:Chris James 译者:Donng 校对:rxcai
本文由 GCTT 原创编译,Go 中文网 荣誉推出