《有效的单元测试》一3.3 使用测试替身的指南

3.3 使用测试替身的指南

测试替身是程序员的工具,就像木匠的锤子和钉子。存在敲钉子的适当方式,当然也有不恰当的方式——最好是能把它们识别出来。
先从我认为最重要的指南开始吧,当你求助于测试替身时要时刻牢记它——从你的工具箱中选择合适的工具。

3.3.1 为测试挑选合适的替身

有许多测试替身可供选择,它们看起来各有千秋。采用它们的最佳条件是什么?到底应该选择哪个?
这里并没有太多的硬性规定,但一般来说你应该因地制宜地混合使用。我是说,某些情况下你只想要“一个返回5的对象”,而其他情况下你特别想知道某个方法被调用过。有时在一个测试中对两者都感兴趣,于是你将Stub、Fake和Mock一同使用。
前面已经说过,并没有清晰的原则来决定采用哪种方式以得到最可读的测试。但我还是忍不住对如何选择这个问题阐述一些逻辑和启发:
如果你关心某些交互,即两个对象之间的方法调用,你可能会需要一个模拟对象Mock。
如果你决定使用Mock,但测试代码最终看起来不像你想的那样漂亮,那就看看一个手工的简单测试间谍Spy能否满足需要。
如果你只关心协作对象向被测对象输送的响应,用桩Stub就可以。
如果你想运行一个复杂场景,其中它所依赖的服务或组件无法供测试使用,而你对所有交互打桩的快速尝试却戛然而止,或产出了难以维护的糟糕的测试代码,那就考虑实现一个伪造对象Fake吧。
如果上述都不能满足你手上的特殊情况,那就抛硬币吧——正面代表Mock,反面代表Stub,如果硬币直立,我允许你找一个Fake帮你干活。
如果觉得那个列表太难记,别怕。《JUnit Recipes》(Manning,2004)的作者J.?B. Rainsberger有一个简单的记忆规则,用于选择正确的测试替身类型:Stub管查询,Mock管操作。现在我们顺利地得到启发,知道什么时候该用哪种测试替身了,接下来看看如何使用它们。

3.3.2 准备、执行、断言

关于编码约定(convention),我要说几句。问题是各种标准太多了。幸运的是,当你构造单元测试时,存在一个大多数程序员都认为合理的、相当确定的实践。它叫做准备-执行-断言(Arrange-Act-Assert),这种组织测试的方式基本上是这样的,先准备用于测试的对象,然后触发执行,最后对输出进行断言。
代码清单3.8复制了代码清单3.7的测试,我们看它如何符合这种约定来组织测试方法。

注意我在三段代码之间增加空白的方式。这用来强调三段代码的不同角色。
测试的前五行是准备所要用到的协作对象。虽然其中我们只涉及Internet接口的一个Mock,但是在测试开头设置多个协作者的情况也很常见。然后是被测对象Translator——对它的实例化也是准备工作的一部分。
下一段代码中,我们调用translation(被测的翻译功能),最后,不论预期输出是直接输出还是造成的副作用,我们都对它进行断言。
给定-当-那么(Given,When,Then)
行为驱动开发运动所推广的词汇和结构与“准备-执行-断言”很像:给定(某个上下文),当(发生某些事情),那么(期望某些结果)。这个想法以更加直观的语言来指定预期行为,尽管“准备-执行-断言”更好记,但“给定-当-那么”更流畅,使人们更加自然地思考行为(而不是实现细节)。
这种结构相当普遍,它有助于使测试保持专注。如果感觉三部分中某一部分很“大”,那就是一个信号,表明测试可能试图做太多事情,需要更加专注。既然说到这话题,咱们就简单讨论一下测试应该专注什么。

3.3.3 检查行为,而非实现

人都会犯错误。模拟对象库新手常犯的一个错误是过度细致地对Mock设置期望。我指的是在测试中,对测试可能涉及的每个对象都做Mock,每个对象间的方法调用都严格指定。
是的,某种意义上,测试给予我们确定性,只要有任何变更它就会中断并报警。而这也是问题所在——即使是最小的变更,哪怕它与测试所要验证的不相关,也会中断测试。好比在一片口香糖上密密麻麻地敲了许多钉子,使之动弹不得。
这种测试的基本问题是缺乏专注。一个测试应当只测试一件事情,并好好地测试,清晰地沟通自己的意图。看着被测对象,你要问自己到底什么是想要验证的预期行为?至于实现细节,倒是并不需要钉在我们的测试中。
预期行为应该配置在Mock对象的期望中。应该寻求通过Stub或非严格Mock来提供实现细节,它们不介意交互从未发生或者发生多次。
检查行为,而非实现。当你掏出喜爱的模拟对象库时,你应该牢牢记住这一点。说到这里……

3.3.4 挑选你的工具

说到模拟对象库,Java程序员真是占了大便宜——有太多可以选择的。像我之前提到的,你几乎可以用任何先进的库来做同样的事情,但是它们在API方面还是有些细微的区别以及一些独特的功能,从而在满足某些特定方向和需求时能够一锤定音。
或许其中最独特的功能就是Mockito的打桩与验证分离。这得细说,接下来看个例子,我用Mockito重写了之前采用JMock的测试:

Mockito的API比JMock更简洁。除此之外,看起来差不多,是不是?是的,只是这个用Mockito写的测试仅仅对方法get()打桩——即使交互从未发生,它也会成功通过。如果我们真的希望验证Translator使用Internet的行为,我们就得增加一个对Mockito API的调用来进行检查:

或者,如果感觉不必那么精确:

模拟对象库API通常是个人喜好问题。但是Mockito在测试风格上有一个明显的优势,那就是主要依赖于打桩——在你的特定上下文中这可能是优势,也可能不是。测试代码每天都保持可读、简洁、可维护,这才是关键。这值得停下来权衡一下,明智地选择工具。
咱们再次借用J.?B.的话来明确JMock与Mockito在方式和适用条件方面的区别:
当我想要拯救遗留代码时,我选择Mockito。当我想要设计新功能时,我选择JMock。
JMock与Mockito不同的前提假设,使得两者擅长不同的任务。默认情况下,JMock认为测试替身(Mock)期望着客户不会在任何时候调用任何方法。如果你想放宽这个假设,你就得增加一个stub。另一方面,Mockito认为测试替身(也叫Mock)允许客户在任何时候调用任何方法。如果你想加强这个假设,那么你就得验证某个方法的调用。这就是区别所在。
不论你决定选择哪个库,我们的第三个即最后一个测试替身指南全都适用。

3.3.5 注入依赖

为了能够使用测试替身,你需要一种替换真实事物的方法。当涉及依赖时——为了测试目的而替换协作对象——我们的指南建议不要在同一个地方同时实例化和使用它们。在实践中,这意味着将这些对象另存为私有成员,或借助工厂方法来获取它们。
一旦你隔离开依赖,你就需要访问它。你可以用可见性修饰符来破坏封装——将私有(private)内容变成公开(public)或者包级私有(package private)——或使用反射API来将测试替身分配给私有字段。那种方式很快就会变得丑陋。更好的选择是采用依赖注入,从外部将依赖传递给对象,通常使用构造函数注入,正如在Translator例子中那样。
我们对于测试替身说得够多了,我渴望进行第二部分了,接下来对本章学到的东西做一个回顾吧。

时间: 2024-09-20 15:00:55

《有效的单元测试》一3.3 使用测试替身的指南的相关文章

使用 Visual Studio Team Test 进行单元测试和java中的测试

原文:使用 Visual Studio Team Test 进行单元测试和java中的测试   C#中test测试地 方法一. 1.从NUnit官网(http://www.nunit.org/index.php)下载最新版本NUnit,当前版本为NUnit2.5.8. 2.安装后,在VS2008中新建测试项目StartNUnit 3.右击项目选择属性,在打开的窗口中选择调试.如图: 4.选择启动外部程序,并定位到NUnit的启动程序nunit.exe.如图: 5.在项目中添加NUnit引用,如图

《有效的单元测试》一3.1 测试替身的威力

3.1 测试替身的威力 甘地(Mahatma Gandhi)说过:"改变世界从自身做起".(Be the change you want to see in the world.)测试替身响应了甘地的召唤,成为你在代码中希望见到的变化.牵强附会?容我慢慢道来.代码是一个大集合.它是指代其他代码的代码网络.每一块都有预定义的行为--作为程序员的你定义了那些行为.某些行为是原子的,包含在单个类或方法中.某些行为意味着不同代码块之间的交互.为了时不时地验证一段代码的行为符合你的期望,最好的选

《有效的单元测试》一3.2 测试替身的类型

3.2 测试替身的类型 你见过了使用测试替身的各种原因,我们也暗示了有多种测试替身可供选择.我们来仔细看看那些类型吧.图3.3展示了这把大伞下的四种对象. 既然我们已经制定了测试替身的分类,现在就来认识一下它们,并了解相互的区别,以及运用它们的典型目的.我们先从最简单的开始. 3.2.1 测试桩通常是短小的 我这样来定义它:桩(名词),截断的或非常短的物体.这衍生出测试桩的精确定义.测试桩(简称桩或Stub)的目的是用最简单的可能实现来代替真实实现.最基本的实现例子就是一个对象的所有方法都只有一

《网络安全测试实验室搭建指南》—第1章1.1节为什么要搭建一个实验室

第1章 搭建硬件及软件的测试平台 网络安全测试实验室搭建指南 有些人希望对信息安全的重要性有更好的理解,而本书恰好是为这一类读者准备的.本章将引导读者搭建一个包含软件和硬件的测试平台.回想一下你的小时候有没有这样的经历,你很好奇电视.收音机甚至电脑是怎么工作的,于是你找来了电烙铁.改锥甚至还有榔头,自己动手把电视机拆了个零零散散.这个过程和你阅读本书学习网络安全的工作原理是类似的,不同的是你的工具不再是改锥和榔头,而是一些安全协议和应用.当然你也会用到一些常用的检测工具,这些工具将会对你分析网络

《网络安全测试实验室搭建指南》目录—导读

版权网络安全测试实验室搭建指南• 著 [美] Michael Gregg 译 曹绍华 刘俊婷 张青锋 刘 玺 责任编辑 傅道坤 • 人民邮电出版社出版发行 北京市丰台区成寿寺路11号 邮编 100164 电子邮件 315@ptpress.com.cn 网址 http://www.ptpress.com.cn • 读者服务热线:(010)81055410 反盗版热线:(010)81055315 版权声明网络安全测试实验室搭建指南Michael Gregg The Network Security

《妙手回春:网站可用性测试及优化指南(修订版)》一导读

内容提要 妙手回春:网站可用性测试及优化指南(修订版)本书是作者Steve Krug继畅销书<点石成金:访客至上的网页设计秘笈>(Don't Make Me Think)后推出的又一力作.多年来,人们就认识到网站可用性测试可以极大地改善产品质量,但鉴于正规的可用性测试流程复杂.费用高昂,很少人这样做.在本书中,作者详细阐述了一种简化的网站可用性测试方法,让任何人都能够尽早并频繁地对其网站.应用程序和其他产品进行可用性测试,从而将最严重的可用性问题消灭在萌芽状态. 本书短小精悍,语言轻松幽默,任

Node.js 单元测试:我要写测试

小明是一个前端工程师,近期因为个人兴趣以及工作上的需要,开始做 Node.js 相关的项目.一个多月过去了,小明基于 Koa 搭出了自己的第一个 Node.js web 应用,在这个过程中,小明也遇到了很多的问题: 如何在上线时保证代码完全没问题? 每次增加功能时如何保证之前的功能是可用的? 随着代码增多,没有勇气和信心去重构代码 面对以上这些问题,小明作为一个前端工程师,惯性思维就是每次部署前先在页面上到处点点,然而一个机智的程序员怎么能把大好时间浪费在这些重复劳动上呢,于是小明就去咨询了组里

将AngularJS的单元测试和端到端测试集成到gradle构建脚本中

我目前工作的一个项目后端使用java和spring建立了一个restful service,前端使用AngularJS来渲染页面,提供用户接口.在前端的AngularJS项目中,我们使用Jasmine来写单元测试,使用AngularJS自带的Angular_scenario来写端到端测试.运行这些测试则使用的是Karma. 虽然使用Karma在命令行下可以很方便的运行所有的测试,但是我们想将这些集成到gradle的构建脚本中,从而将AngularJS的所有测试加入到CI的构建中.同时为了保证运行

《网络安全测试实验室搭建指南》—第1章1.3节软件要求

1.3 软件要求 本节我们关注你的实验室里的软件要求,包括操作体统和应用软件.你可能会疑惑该如何选择正确的操作系统或者怎样判断哪款操作系统才是你最需要的.在搭建网络安全实验室过程中,你使用的软件起着至关重要的作用.如果你的预算紧张,那么正确选择适合你的软件尤为重要,有几款软件是你必须安装的. 最大程度利用你的预算的一个办法就是购置虚拟机.这个选择节省硬件的开销因而性价比非常高.此外你也需要考虑一下哪些应用程序和工具需要安装在新系统里.最后不要忘了你搭建这个测试实验室的最终目标:实验室是专门用来做