存根类 测试代码 java_测试双打:模拟,假人和存根

存根类 测试代码 java

大多数班级都有合作者。 在进行单元测试时,您通常希望避免使用那些协作者的实际实现方式来避免测试的脆弱性和绑定/耦合,而应使用测试双打:模拟,存根和双打。 本文引用了有关该主题的两篇现有文章:Martin Fowler的Mocks Are n't Stubs和Bob Uncle的The Little Mocker 。 我都推荐他们。

术语

我将从Gerard Meszaros的书xUnit Test Patterns中借用一个术语。 在其中,他引入了术语“ 被测系统( SUT )”,即我们正在测试的东西。 “测试中的类”是更适用于面向对象的世界的替代方法,但是我会坚持使用SUT,因为Fowler也会这样做。

我还将使用状态验证行为验证这两个术语。 状态验证是通过检查SUT或其协作者的状态来验证代码是否正常工作。 行为验证是在验证协作者是否按照我们期望的方式被调用或调用。

测试双打

好,回到如何与被测系统的合作者打交道。 对于SUT的每个协作者,您可以使用该协作者的实际实现。 例如,如果您有一个与数据访问对象(DAO)协作的服务,如下面的WidgetService示例中所示,则可以使用真实的DAO实现。 但是,它很可能与数据库冲突,这绝对不是我们要进行单元测试所需的数据库。 另外,如果DAO实现中的代码发生更改,则可能导致我们的测试开始失败。 我个人不喜欢当被测代码本身未更改时测试开始失败。

因此,我们可以使用有时称为“测试双打”的测试。 “测试双打”一词也来自Meszaros的xUnit测试模式书。 他将它们描述为“为明确运行测试而安装的任何代替实际组件的对象或组件”。

在本文中,我将介绍我使用的三种主要的测试双打类型:模拟,存根和傻瓜。 我还将简要介绍两个我很少明确使用的东西:间谍和假货。

1.嘲弄

首先,“模拟”是一个重载术语。 它通常用作任何测试双精度测试的总称; 也就是说,任何类型的对象都可以代替被测类中的真实协作者。 我对此感到满意,因为大多数模拟框架都支持此处讨论的大多数测试双打。 但是,出于本文的目的,我将在更严格,更有限的意义下使用模拟。

具体来说, 模拟是一种使用行为验证的测试替身类型

马丁·福勒(Martin Fowler)将模拟描述为“用期望进行预编程的对象,这些对象构成了期望接收的调用的规范”。 正如Bob叔叔所说的那样,模拟程序会监视正在测试的模块的行为,并且知道期望的行为。 一个例子可以使它更清楚。

想象一下WidgetService的实现:

public class WidgetService {final WidgetDao dao;public WidgetService(WidgetDao dao) {this.dao = dao;}public void createWidget(Widget widget) {//misc business logic, for example, validating widget is valid//...dao.saveWidget(widget);}
}

我们的测试可能看起来像这样:

public class WidgetServiceTest {//test fixturesWidgetDao widgetDao = mock(WidgetDao.class);WidgetService widgetService = new WidgetService(widgetDao);Widget widget = new Widget();@Testpublic void createWidget_saves_widget() throws Exception {//call method under testwidgetService.createWidget(widget);//verify expectationverify(widgetDao).saveWidget(widget);}
}

我们创建了一个WidgetDao的模拟,并验证它是否如预期的那样被调用。 我们还可以告诉模拟程序在调用时如何响应。 这是模拟的重要组成部分,允许您操纵模拟,以便可以测试代码的特定单元,但是在这种情况下,测试不是必需的。

模拟框架

在此示例中,我将Mockito用于模拟框架,但是Java空间中还有其他对象,包括EasyMock和JMock 。

自己动手玩吗?

请注意,您不必使用模拟框架即可使用模拟。 您也可以自己编写模拟,甚至可以在模拟中构建断言。 例如,在这种情况下,我们可以创建一个名为WidgetDaoMock的类,该类实现WidgetDao接口,并且该类的createWidget()方法的实现仅记录其被调用的情况。 然后,您可以验证呼叫是否按预期进行。 尽管如此,现代的模拟框架仍然使这种“劳碌自在”的解决方案变得多余。

2.存根

存根是为了测试目的而“存根”或提供该实现的极大简化版本的对象。

例如,如果我们的WidgetService类现在也也依赖于ManagerService。 请参阅此处的标准化方法:

public class WidgetService {final WidgetDao dao;final ManagerService manager;public WidgetService(WidgetDao dao, ManagerService manager) {this.dao = dao;this.manager = manager;}public void standardize(Widget widget) {if (manager.isActive()) {widget.setStandardized(true);}}public void createWidget(Widget widget) {//omitted for brevity}
}

并且我们想测试当管理器处于活动状态时,标准化方法是否“标准化”了一个小部件,我们可以使用如下所示的存根:

public class WidgetServiceTest {WidgetDao widgetDao = mock(WidgetDao.class);Widget widget = new Widget();class ManagerServiceStub extends ManagerService {@Overridepublic boolean isActive() {return true;}}@Testpublic void standardize_standardizes_widget_when_active() {//setupManagerServiceStub managerServiceStub = new ManagerServiceStub();WidgetService widgetService = new WidgetService(widgetDao, managerServiceStub);//call method under testwidgetService.standardize(widget);//verify stateassertTrue(widget.isStandardized());}
}

由于模拟通常用于行为验证,而存根可用于状态或行为验证。

这个例子非常基础,也可以使用模拟来完成,但是存根可以为测试夹具的可配置性提供有用的方法。 我们可以对ManagerServiceStub进行参数化,以使其将“活动”字段的值用作构造函数参数,因此可以在否定测试用例中重用。 也可以使用更复杂的参数和行为。 其他选项是将存根创建为匿名内部类,或者为存根创建基类,例如ManagerServiceStubBase,以供其他人扩展。 后者的优点是,如果ManagerService接口发生更改,则只有ManagerServiceStubBase类会中断,并且需要更新。

我倾向于经常使用存根。 我喜欢他们提供的灵活性,以便能够自定义测试装置,并提供纯Java代码提供的清晰度。 将来的维护者不需要能够理解某个框架。 我的大多数同事似乎更喜欢使用模拟框架。 找到最适合您的方法并运用最佳判断。

3.假人

顾名思义,虚拟对象是非常愚蠢的类。 它几乎不包含任何内容,基本上只足以使您的代码得以编译。 当您不在乎如何使用虚拟对象时,可以将其传递给某个对象。 例如,作为测试的一部分,当您必须传递参数时,但是您不希望使用该参数。

例如,在前面的示例中的standardize_standardizes_widget_when_active()测试中,我们仍然继续使用模拟的WidgetDao。 虚拟对象可能是一个更好的选择,因为我们根本不希望在createWidget()方法中完全使用WidgetDao。

public class WidgetServiceTest {Widget widget = new Widget();class ManagerServiceStub extends ManagerService {@Overridepublic boolean isActive() {return true;}}class WidgetDaoDummy implements WidgetDao {@Overridepublic Widget getWidget() {throw new RuntimeException("Not expected to be called");}@Overridepublic void saveWidget(Widget widget) {throw new RuntimeException("Not expected to be called");}}@Testpublic void standardize_standardizes_widget_when_active() {//setupManagerServiceStub managerServiceStub = new ManagerServiceStub();WidgetDaoDummy widgetDao = new WidgetDaoDummy();WidgetService widgetService = new WidgetService(widgetDao, managerServiceStub);//call method under testwidgetService.standardize(widget);//verify stateassertTrue(widget.isStandardized());}
}

在这种情况下,我创建了一个内部类。 在大多数情况下,由于Dummy功能很少会在测试之间发生变化,因此创建一个非内部类并为所有测试重用更为有意义。

还要注意在这种情况下,使用模拟框架创建类的模拟实例也是可行的选择。 我个人很少使用假人,而是创建这样的模拟:

WidgetDaoDummy widgetDao = mock(WidgetDao.class);

尽管可以肯定的是,当确实发生意外调用时,抛出异常会更加困难(取决于您选择的模拟框架),但是它确实具有简洁性的巨大优势。 虚拟变量可能很长,因为它们需要在接口中实现每种方法。

与存根一样,假人可用于状态或行为验证。

间谍与伪造

我将简要介绍另外两种测试双打:间谍和伪造。 我之所以简短地说,是因为我个人很少自己明确使用这两种类型的双打,而且还因为术语可能会引起混乱,而又不引入更多细微差别! 但是为了完整性……

间谍

当您想确保系统调用了某个方法时,可以使用间谍程序。 它还可以记录各种事情,例如计算调用次数,或记录每次传递的参数。

但是,对于间谍来说,存在将测试与代码实现紧密耦合的危险。

间谍专用于行为验证。

大多数现代的模拟框架也很好地涵盖了这种功能。

假货

马丁·福勒(Martin Fowler)对伪造品的描述如下:伪造品具有有效的实现,但通常采取一些捷径,这使其不适合生产(内存数据库是一个很好的例子)。

我个人很少使用它们。

结论

测试双打是单元测试不可或缺的一部分。 嘲讽,存根和双打都是有用的工具,了解区别非常重要。

从最严格的意义上讲,模拟只是使用行为验证的双精度。 指定了两倍的期望值,然后在调用SUT时进行验证。 但是,工作模拟也已经越来越广泛地描述了此处描述的任何双打,实际上大多数现代模拟框架都可以以这种通用方式使用。

最后,您应该使用哪种双精度型? 这取决于要测试的代码,但是我建议您以使您的测试意图最清楚的方式为指导。

资料来源

  • 莫蒂不是存根作者 ,马丁·福勒(Martin Fowler)
  • 小嘲笑 ,“叔叔”鲍勃·马丁
  • xUnit测试模式 ,作者Gerard Meszaros

翻译自: https://www.javacodegeeks.com/2015/11/test-doubles-mocks-dummies-and-stubs.html

存根类 测试代码 java

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/337063.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

5 个牛逼的算法设计,你知道几个?

点击蓝字关注我们因公众号更改推送规则,请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络,侵删1、分治法概念:将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之…

python2.7.10安装教程_Linux系统(CentOS)下python2.7.10安装

本文记录了Linux系统(CentOS)安装Python,供大家参考,具体内容如下Python(Linux) 下载地址操作系统:Centos6.41、下载下载的版本:Python-2.7.10.tgz2、安装一般情况下,Linux都会预装 Python了,但版本较低。(…

javafx窗体程序_JavaFX实际应用程序:AISO HRC-Matic

javafx窗体程序“ Real-World JavaFX Apps”系列中的第三个应用程序是一种重型数据输入应用程序,由称为HRC-Matic的关系数据库支持。 它由AISO在日内瓦开发。 AISO是一家专门开发基于JavaFX的业务应用程序的公司。 他们还在研究我在本系列的第一个博客( …

x201换风扇_笔记本怎么换风扇 ThinkPad X201i换风扇图文教程

ThinkPad X201i换电扇图文教程:拆机之前,我们需求先对X201i的散热电扇在停止了开端的理解,得知价钱从10元左右的单电扇,到上百的散热全体都有,而且还分东芝产和松下产等不同产地的,小编选择了松下产的整套散…

几十个Shell分析日志文件脚本!

点击蓝字关注我们因公众号更改推送规则,请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络,侵删收集,整理一些服务器日志分析命令,可以用来分析自己网站服务器日志, 看看网站的访问量。看看有没有黑阔搞破坏…

flex布局水平垂直 垂直_垂直和水平装饰

flex布局水平垂直 垂直装饰器模式是在不更改其接口的情况下向对象添加功能的最佳方法之一。 我经常使用可组合装饰器,并且总是会问自己在功能列表必须可配置时如何正确设计它们。 我不确定我的答案是否正确,但是这里有一些值得深思的地方。 The Apartme…

一文读懂 | Linux 中的各种栈:进程栈 线程栈 内核栈 中断栈

点击蓝字关注我们因公众号更改推送规则,请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络,侵删栈是什么?栈有什么作用?首先,栈 (stack) 是一种串列形式的数据结构。这种数据结构的特点是后入先出 (LIFO, L…

thinkphp6企业项目实战_[MarsZ]ThinkPHP项目实战总结

本文原word格式百度云盘下载地址:综述 2需求 2作者 2系统一览 2Unity3d客户端 2PHP后台 4准备 6XAMPP 6官网 6下载地址 6教程 6ThinkPHP 6官网 7项目所用版本 7下载地址 7快速入门 7完全手册 7步骤 7准备ThinkPHP 7修改MySQL设置信息 8检测是否成功 9扩展&#xff1…

java包装模式_在Java8中包装设计模式

java包装模式GOF书中没有列出环绕模式,但对于以下问题非常有用: 循环构造,例如执行while / while / for循环 秒表周围的一些代码。 用运行时异常包装检查的异常 初始化和清理,例如创建/销毁线程池或打开/关闭文件等 将上下文…

c得到当前时分秒 linux_Linuxc - Linux系统下的时间知识点

Unix/Linux系统下的时间知识点一、Unix/Linux系统的几种时间结构:1、time_t 类型:长整型,一般用来表示从1970-01-01 00:00:00时以来的秒数,精确度:秒;由函数time()获取;该类型定义在头文件 /usr…

【C语言】彻底搞懂内存屏障与volatile

点击蓝字关注我们因公众号更改推送规则,请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络,侵删最有价值的写在最前面内存屏障与 volatile 是高并发编程中比较常用的两个技术,无锁队列的时候就会用到这两项技术。然而这两项技术涉…

javafx 使用_何时使用JavaFX代替HTML

javafx 使用像我这样的JavaFX爱好者反复面对的一个问题是何时(或为什么)使用JavaFX代替HTML(5)。 这是我的两分钱: 如果…,则应使用JavaFX。 …您对坚固性/质量感兴趣。 JavaFX是Java! 您将能…

charles代理手机调试_H5开发 移动端 调试之 Charles 抓包 和 Map Remote

移动端调试是每一位前端工程师必须掌握的技能,在移动端环境上往往有很多不同的情况,我们需要借助一些工具来调试。本文将介绍 Mac 如何安装 charles,进行抓包、以及一些应用场景。场景一:app端内线上环境接口报错,想看…

lucene 查询示例_Lucene查询(搜索)语法示例

lucene 查询示例本文是我们名为“ Apache Lucene基础知识 ”的学院课程的一部分。 在本课程中,您将了解Lucene。 您将了解为什么这样的库很重要,然后了解Lucene中搜索的工作方式。 此外,您将学习如何将Lucene Search集成到您自己的应用程序中…

熟悉又陌生的arm 编译器详解(armcc/armclang)

点击蓝字关注我们因公众号更改推送规则,请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络,侵删arm编译器学习首先来了解一下编译器,其通常分为三个部分:前端优化器后端。前端:词法、语法和语义分析&#x…

js怎么获取一个元素与屏幕右边的距离_js如何获取div(ul li)离屏幕上下左右边距离(长度)...

在网页中移到元素div(或 ul li),常常需要获取元素div离屏幕上下左右的距离,这样可以知道元素处在屏幕中的位置,以控制元素的移动。获取元素div的长度或高度,可以用 js 获取元素div的width或height属性得到,但元素没有距…

图文详解STM32单片机远程升级

点击蓝字关注我们因公众号更改推送规则,请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络,侵删1、需要两份程序BootLoader和App程序,两份程序均可以通过jlink下载,只需要将下载地址修改一下即可:2、在Boot…

建立索引lucene_用Lucene建立搜索索引

建立索引lucene本文是我们名为“ Apache Lucene基础知识 ”的学院课程的一部分。 在本课程中,您将了解Lucene。 您将了解为什么这样的库很重要,然后了解Lucene中搜索的工作方式。 此外,您将学习如何将Lucene Search集成到您自己的应用程序中…

hadoop 卡主_HDFS DisTCP执行卡住了,怎么办?

背景我们在进行两个集群间数据同步的时候,使用的是hdfs的distcp的方式进行跨集群跨版本的数据同步,但是在执行hdfs distcp 命令时,发现在运行到 with build listing处就卡住了 .具体问题如下图:针对问题解决,中间我们试过了哪些办法1 首先查看hdfs本身服务状态是否正常,get命令…

spring javafx_Oracle Spring Clean JavaFX应该吗?

spring javafx我们确实在Codename One上依赖JavaFX,我们的模拟器需要它。 我们的桌面版本使用它,而我们的设计器工具基于Swing。 我们希望它成功,这对我们的业务至关重要! 即使您是Java EE开发人员并且不关心桌面编程,…