文章目录

  • 一、前言
  • 二、Fake
  • 三、Stub
  • 四、Mock

原文:https://blog.pragmatists.com/test-doubles-fakes-mocks-and-stubs-1a7491dfa3da

一、前言

自动化测试中,我们常会使用一些经过简化的,行为与表现类似于生产环境下的对象的复制品。引入这样的复制品能够降低构建测试用例的复杂度,允许我们独立而解耦地测试某个模块,不再担心受到系统中其他部分的影响;这类型对象也就是所谓的 Test Double。

实际上对于 Test Double 的定义与阐述也是见仁见智,Gerard Meszaros 在这篇文章:http://xunitpatterns.com/Test%20Double.html 中就介绍了五个不同的 Double 类型;而人们更倾向于使用 Mock 来统一描述不同的 Test Doubles。不过对于 Test Doubles 实现的误解还是可能会影响到测试的设计,使测试用例变得混乱和脆弱,最终带来不必要的重构。

本文则是从作者个人的角度描述了常见的 Test Doubles 类型及其具体的实现:Fake、Stub 与 Mock,并且给出了不同的 Double 的使用场景。

二、Fake

Fakes are objects that have working implementations, but not same as production one. Usually they take some shortcut and have simplified version of production code.
Fake 是那些包含了生产环境下具体实现的简化版本的对象。

如下图所示,Fake 可以是某个 Data Access Object 或者 Repository 的基于内存的实现;该实现并不会真的去进行数据库操作,而是使用简单的 HashMap 来存放数据。这就允许了我们能够在并没有真的启动数据库或者执行耗时的外部请求的情况下进行服务的测试。

@Profile("transient")
public class FakeAccountRepository implements AccountRepository {Map<User, Account> accounts = new HashMap<>();public FakeAccountRepository() {this.accounts.put(new User("john@bmail.com"), new UserAccount());this.accounts.put(new User("boby@bmail.com"), new AdminAccount());}String getPasswordHash(User user) {return accounts.get(user).getPasswordHash();}
}

除了应用到测试,Fake 还能够用于进行原型设计或者峰值模拟中;我们能够迅速地实现系统原型,并且基于内存存储来运行整个系统,推迟有关数据库设计所用到的一些决定。另一个常见的使用场景就是利用 Fake 来保证在测试环境下支付永远返回成功结果。

三、Stub

Stub is an object that holds predefined data and uses it to answer calls during tests. It is used when we cannot or don’t want to involve objects that would answer with real data or have undesirable side effects.
Stub 代指那些包含了预定义好的数据并且在测试时返回给调用者的对象。Stub 常被用于我们不希望返回真实数据或者造成其他副作用的场景。

Stub 的典型应用场景即是当某个对象需要从数据库抓取数据时,我们并不需要真实地与数据库进行交互或者像 Fake 那样从内存中抓取数据,而是直接返回预定义好的数据。

public class GradesService {private final Gradebook gradebook;public GradesService(Gradebook gradebook) {this.gradebook = gradebook;}Double averageGrades(Student student) {return average(gradebook.gradesFor(student));}
}

我们在编写测试用例时并没有从 Gradebook 存储中抓取数据,而是在 Stub 中直接定义好需要返回的成绩列表;我们只需要足够的数据来保证对平均值计算函数进行测试就好了。

public class GradesServiceTest {private Student student;private Gradebook gradebook;@Beforepublic void setUp() throws Exception {gradebook = mock(Gradebook.class);student = new Student();}@Testpublic void calculates_grades_average_for_student() {when(gradebook.gradesFor(student)).thenReturn(grades(8, 6, 10)); //stubbing gradebookdouble averageGrades = new GradesService(gradebook).averageGrades(student);assertThat(averageGrades).isEqualTo(8.0);}
}

Command Query Separation

仅返回部分结果而并没有真实改变系统状态的的方法被称作查询(Query)。譬如 avarangeGrades,用于返回学生成绩平均值的函数就是非常典型的例子:
Double getAverageGrades(Student student);

该函数仅返回了某个值,而没有其他的任何副作用。正如我们上文中介绍的,我们可以使用 Stubs 来替换提供实际成绩值的函数,从而简化了整个测试用例的编写。

不过除了 Query 之外还有另一个类别的方法,被称作 Command。即当某个函数在执行某些操作的时候还改变了系统状态,不过该类型函数往往没有什么返回值:
void sendReminderEmail(Student student);

这种对于方法的划分方式也就是 Bertrand Meyer 在 Object Oriented Software Construction: 一书中介绍的 Command Query 分割法。

  • https://www.amazon.com/Object-Oriented-Software-Construction-Book-CD-ROM/dp/0136291554

对于 Query 类型的方法我们会优先考虑使用 Stub 来代替方法的返回值,而对于 Command 类型的方法的测试则需要依赖于 Mock。

四、Mock

Mocks are objects that register calls they receive. In test assertion we can verify on Mocks that all expected actions were performed
Mocks 代指那些仅记录它们的调用信息的对象,在测试断言中我们需要验证 Mocks 被进行了符合期望的调用。

当我们并不希望真的调用生产环境下的代码或者在测试中难于验证真实代码执行效果的时候,我们会用 Mock 来替代那些真实的对象。典型的例子即是对邮件发送服务的测试,我们并不希望每次进行测试的时候都发送一封邮件,毕竟我们很难去验证邮件是否真的被发出了或者被接收了。

我们更多地关注于邮件服务是否按照我们的预期在合适的业务流中被调用,其概念如下图所示:

public class SecurityCentral {private final Window window;private final Door door;public SecurityCentral(Window window, Door door) {this.window = window;this.door = door;}void securityOn() {window.close();door.close();}
}

在上述代码中,我们并不想真的去关门来测试 securityOn 方法,因此我们可以设置合适的 Mock 对象:

public class SecurityCentralTest {Window windowMock = mock(Window.class);Door doorMock = mock(Door.class);@Testpublic void enabling_security_locks_windows_and_doors() {SecurityCentral securityCentral = new SecurityCentral(windowMock, doorMock);securityCentral.securityOn();verify(doorMock).close();verify(windowMock).close();}
}

在 securityOn 方法执行之后,window 与 door 的 Mock 对象已经记录了所有的交互信息,这就允许我们能够去验证 Window 与 Door 是否被真实的调用。或许有人会疑问是否在真实环境下门与窗是否被真的关闭了?其实我们并不能保证,不过这也不是我们关注的点,也不是 SecurityCentral 这个类关注的目标。门与窗是否能被正常的关闭应该是由 Door 与 Window 这两个类所关注的。

深入阅读:

  • Test Double - Martin Fowler
  • Test Double - xUnit Patterns
  • Mocks Aren’t Stubs - Martin Fowler
  • Command Query Separation - Martin Fowler

(翻译)测试替身— Fakes, Mocks 和 Stubs相关推荐

  1. Node.js Sinon测试替身

    测试替身 测试替身(Test Double),顾名思义,即测试时用来代替系统中某一部分的模拟技术的统称. 测试替身的作用 隔离被测代码 加速执行测试 使执行变得确定 模拟特殊情况 访问隐藏信息 上述列 ...

  2. 计算机英语翻译测试稿,英语演讲稿_四六级写作翻译语法检测工具推荐_沪江英语...

    不少同学在做四六级真题的时候,都直接跳过写作翻译部分.觉得自己写了也没有人可以帮忙检查,自己也不知道错在哪里,写了又浪费时间. 所以这一次推荐的写作检测工具,就可以解决大家的烦恼啦!英语作文还是要经常 ...

  3. [论文翻译]测试时数据增强(TTA):Automatic Brain Tumor Segmentation using Convolutional Neural Networks with TTA

    论文下载: 地址 Automatic Brain Tumor Segmentation using Convolutional Neural Networks with Test-Time Augme ...

  4. 计算机英语翻译测试稿,英语翻译中级口译模拟测试及答案.doc

    英语翻译中级口译模拟 SECTION 1: LISTENING TEST A: Spot Dictation You might enjoy a cup of coffee at your local ...

  5. 三大翻译牛站,在线翻译测试,又是google的最好

    更多请访问 http://www.123de6.cn <script type="text/javascript"> google_ad_client = " ...

  6. 技术文章系列整理(持续更新)

    本博客有很多关于技术的文章,我试着对你可能感兴趣的一些重要文章进行分类. 文章目录 一.基础 二.性能 三.Java接口测试 四.移动端测试 五.DevOps 六.运维 七.团队管理 八.大数据 九. ...

  7. 单元测试探析:什么是Stubs、Mocks、Spies、Dummies?带你了解4个核心工具

    在单元测试中,对象之间的依赖往往交织到一起,需要拆成各个单元才能逐个击破,这也是单元测试的目的.如何将这些交织到一起的对象拆开,需要一些工具,这些工具业内人们称其为"测试替身". ...

  8. Software Engineering at Google翻译-III-11-Testing overview(测试概述)

    参考: https://github.com/daizhenhong/swe-at-google/blob/main/Part_III_Processes/total/Chapter-11-total ...

  9. 测试中使用定位搜索框搜索内容_翻译技术 | 搜索神器:Wox+Everything使用教程

    搜索神器:Wox+Everything使用教程 1. 工具简介 1.1 Everything Everything是一款占用内存低.搜索速度快的本地搜索软件,可以实现对于电脑本地硬盘中文件的的快速搜索 ...

最新文章

  1. 顺F速运,你被爱加M坑了
  2. 这个B站up主太硬核了!纯手工打造AI小电视:硬件自己焊接,驱动代码全手写...
  3. B-Trees【设计数据密集型应用】
  4. 如何:对 SharePoint 列表项隐藏 ECB 中的菜单项
  5. python处理excel表格教程-python操作excel(内附python教程分享)
  6. ACM入门之【线段树】
  7. 《实施Cisco统一通信管理器(CIPT1)》一2.8 复习题
  8. 面试官:给我说一下你理解的分布式架构
  9. 51单片机学习笔记之定时器程序设计
  10. 】.NET使用NPOI组件将数据导出Excel
  11. 周鸿祎:做产品体验先把自己切换到二傻子模式
  12. 集合阿里云、达摩院、平头哥相关技术的HaaS,官宣出书啦
  13. JS中5秒中跳转到其他页面
  14. 构造函数SimpleAdapter()
  15. 计算机未来发展展望3000字,关于我的计算机职业生涯规划3000字
  16. 计算机在英语翻译上的运用,有道如何在网页上翻译一句话,有道网页上的字翻译怎么用...
  17. mac笔记本怎么外接显示屏_苹果MAC笔记本怎么外接显示器
  18. 做一个FLASH游戏你需要掌握的东西【实用】
  19. 暗黑2魔电西格玛攻略_魔电西格玛,暗黑2最佳继承者-1949游戏测评
  20. CTF Crypton系列————4、考眼力

热门文章

  1. autograd-自动求导系统
  2. mysql自然连接的例题详解_基于 MySQL 的数据库实践(自然连接)
  3. jshint详细说明【vscode插件】
  4. 一文助你搞懂单纯形法
  5. C语言-XLSX基础数据解析(需借助libxml2库)
  6. libxml2的安装和使用
  7. CUDA编程:矩阵乘运算从CPU到GPU
  8. 常用密码的正则表达式
  9. Mac电脑QuickTime Player不支持的播放格式
  10. 【图像去噪】基于matlab GUI均值+中值滤波图像去噪(含PNSR)【含Matlab源码 372期】