携手创造,共同成长!这是我参加「日新方案 8 月更文应战」的第17天,点击检查活动详情

今日持续咱们的【解析Golang测验】第四篇,对此前文章感兴趣的同学能够点击进入:

  • 解析 Golang 测验(1)- 原生支撑
  • 解析 Golang 测验(2)- gomock
  • 解析 Golang 测验(3)- goconvey 实战

日常开发测验中,咱们常常遇到各种【代替目标】的叫法,mock,stub,fake,dummy,许多时分差异不开到底哪个是哪个,今日这篇文章,咱们就来看看这些概念,以及 Test Double 怎样差异。

Test Double

  • SUT:System Under Test,也就是待测的体系。

注意这个是看站在谁的视点,比方 A 体系需求和 B 体系交互完成一件事,当咱们把 B 体系给替换了一个假完成,就是测验 A 的话,这儿 A 就是 SUT。

  • Test Double

Test doubles are objects that stand in for other objects during a test.

咱们通常所说的 mock 其实是一系列总称,事实上这一类【用来代替 SUT 中某个不需求测验的目标】的目标都能够被称为 Test Double。比方咱们上面的例子,我此非必须测验 A 体系,就这一件工作上看,B 体系详细怎样我是不关心的,只需契合标准来返回数据即可,同时为了避免直接依靠,污染数据,所以我需求一个【代替目标】,来作为一个【假的B体系】供 A 体系调用测验,这就是 Test Double。

并且由于有一个咱们能够操控的 Test Double,许多场景咱们都能够结构出来,这就是所谓的:

Control the behavior of our dependencies.

这一切的底子依据在于:此时此刻,咱们需求验证的是,A 体系在遇到 B 体系各种 “契合交互标准” 的呼应之后,它该怎样处理的问题。而不是 B 体系是否正常运作。

Test Double 包含了 dummy, fake, mock, stub, spy 五种不同的类型,这儿咱们引证 Martin Fowler 的经典论述:

  • Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists.
  • Fake objects actually have working implementations, but usually take some shortcut which makes them not suitable for production (an in memory database is a good example).
  • Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what’s programmed in for the test.
  • Spies are stubs that also record some information based on how they were called. One form of this might be an email service that records how many messages it was sent.
  • Mocks are what we are talking about here: objects pre-programmed with expectations which form a specification of the calls they are expected to receive.

简略总结一下:

  • Dummy:仅仅个 placeholder,放在那儿没有实践作用,不会真的包含任何逻辑;
  • Fake:对一些体系进行裁剪之后构成的可运转的一套完成,跟源体系比较有一些(乃至很大)差异,不能上生产环境,可是作为测验使用非常适合,能提早暴露许多问题,例如一个内存版的数据库
  • Stub:提早预设好一些呼应返回,不会跟其他体系有交互;
  • Spy:相当于 Stub 加强版,不只仅有预先界说的数据,还会记录一些被调用时分的信息(比方每次调用都把数据加到一个行列里)。比方咱们针对一个发邮件服务做 spy,调用结束后,需求看下调用了几次,这时分就用到了这份信息;
  • Mock:重点在于 expectation!也就是说,咱们关于这次【调用】在遇到各种情况时应该怎样处理,提早指定好标准)。

其中,Dummy 和 Fake 好理解,一个是没啥用,仅仅占位符,另一个是基本上啥都能干,比实在的体系差点意思,但基本上能覆盖大部分场景。而关于 spy,通常咱们不太差异它和 stub,能够一起理解。

那么问题来了,Mock 说的是你要明确你对每次调用的 expectation,需求写代码来指明什么情况下要怎样做,而 Fake 如同也是这个意思,差异在于这个代码或许不用你写(由于开源社区有一些现成可用的)。那么它们底子差异是什么呢?

把握住这三点即可:

  • Fake => working implementations
  • Mock => predefined behavior
  • Stub => predefined values

In state verification you have the object under testing perform a certain operation, after being supplied with all necessary collaborators. When it ends, you examine the state of the object and/or the collaborators, and verify it is the expected one.

In behaviour verification, on the other hand, you specify exactly which methods are to be invoked on the collaboratos by the SUT, thus verifying not that the ending state is correct, but that the sequence of steps performed was correct.

Mock 和 Stub 的差异在于,前者是行为,后者是状态。基于 Mock 做的是 behavior-based verification, 基于 Stub 做的是 State-based verification,这跟验证的方法有关。而 Mock 和 Fake 的差异在于,你在写单测的时分,需不需求构建出一个 working implementation,还是说只需预设一些行为的呼应即可。

Fake

好了,有了根底的认知,咱们就能够进入正题了,咱们常用的 MySQL 和 redis 的内存 mock,本质上就是上面说的 fake,也就是说,一个直接可用的平替。ok,有的同学又要问了,那为啥针对这种存储,我要用 Fake 呢?我直接把整个 Repository 给 Mock 了不行么?

代替目标的含义

行,当然能够。但问题回来了,咱们做 Mock 的原因是什么?

  • 咱们不期望依靠环境,咱们期望单测是安稳的。不然一旦你的某个环境,那个实在的存储由于网络原因或磁盘等原因暂时不能服务了,你连单测都跑不了。

  • 咱们期望单测是快速的,如果你每次跑完一个项目的单测都需求十几分钟,验证一个根底的点都要花很长时刻,那么会大大下降人们经过单测来发现问题的动力,所以,起 docker 来跑实在存储也 ok,但最好放到 CI,集成测验的流程,而不是单测。

而这一切的根底是什么呢?是我用代替目标,不影响测验的准确性。由于能被我 mock 的,必定不是这非必须测验,要验证的组件。你不或许分明要测验某个下流服务,却还将其 mock 掉,换成一个假完成。

所以,Test Double 存在的含义在于:关于【并非此非必须测验的体系】,用一个【代替目标】来简化处理,让咱们能够低成本,快速地验证咱们需求验证的部分。

Why Fake?

我用 Mock 能不能达到一样的作用?能,但注意,代替目标咱们能够操控,但也需求遵循必定的标准,这个标准就是上下流的协议。

举个例子,我期望下流 X 服务给我一个班里学生的数量,接口姓名叫做 GetStudentCount,传入一个班号即可,函数签名相似这样: func GetStudentCount(classNo int) int

针对这个函数来 mock 一点都不复杂,为什么?底子原因在哪儿?

在于这个入参出参极其简略!如果连这个参数都传错,那就是你开发者自己的初级问题了,单测救不了你。

被咱们隐含的复杂性在于:出参入参的协议。这一点在许多场景下都比较简略,所以咱们忽略了,但针对 MySQL,Redis 这一类界说了自己的一套 DML 的语言,你不必定真的能按照想法来写出对的句子!

这个时分,谁能来确认你的用法,写法对不对呢?Fake 就能够来帮忙!

Fake 能够供给一套,从交互,协议层面完全一致(或至少90%一致)的 working implementation,你直接用即可,仅限测验,这样能最大程度削减由于用法不对导致的问题。

下一篇文章,咱们将会一起来看一看,MySQL 和 Redis 有哪些 Fake 能够用,敬请期待。

参考资料

  • Mocks Aren’t Stubs
  • Test Doubles — Fakes, Mocks and Stubs.
  • What is faking vs. mocking vs. stubbing?
  • What’s the difference between faking, mocking, and stubbing?
  • State vs Behaviour Verification