双重for_测试双重图案

双重for

前段时间,我写了一篇有关使用Test Double的后果的文章,但是与Test Double Patterns无关,仅是一个简单的清单。 今天,我想对其进行更改,并解释这些模式之间的差异。

正如我在提到的文章中写道:

Test Double是允许我们控制被测单元之间依赖性的模式。 为了能够在我们想要或/和/或验证是否发生想要的行为时提供想要的行为。
因此,现在当您想起了基础知识时,我们可以转到有趣的部分–让我们看一下“测试双重模式”。

虚拟对象

虚拟是TD(测试双精度),当我们想要传递对象以填充参数列表时使用。 从未实际使用过。 这就是为什么它不总是被视为TD之一的原因-它不提供任何行为。

假设我们有发送报告的Sender类。 由于某些要求,我们需要将其包装到另一个类中以提供有效的接口。 我们的课看起来像这样:

public class ReportProcessor implements Processor {private Sender sender;public ReportProcessor(Sender sender) {this.sender = sender;}@Overridepublic void process(Report report) {sender.send(report);}
}

现在,我们的测试是什么样的? 我们需要验证什么? 我们必须检查报告是否传递给Sender实例的send()方法。 可以按照以下步骤完成:

public class DummyTest {@Testpublic void shouldSentReportWhileProcessing() {Sender sender = aMessageSender();ReportProcessor reportProcessor = aReportProcessor(sender);Report dummyReport = new Report();reportProcessor.process(dummyReport);then(sender).should().send(dummyReport);}private ReportProcessor aReportProcessor(Sender sender) {return new ReportProcessor(sender);}private Sender aMessageSender() {return spy(Sender.class);}
}

如您所见,没有与我们的虚拟对象进行交互。 仅创建报告并将其作为参数传递。 没有行为,只有存在。

假物件

Fake Object只是测试类所依赖的对象的一种更简单,更轻量的实现。 它提供了预期的功能。

在决定时要记住的重要事项是使其尽可能简单。 任何其他逻辑可能会对测试的脆弱性和准确性产生重大影响。

假设我们有一个带有create()方法的ReportService,它的职责是仅在尚未创建Report的情况下创建一个Report。 为简单起见,我们可以假定标题标识一个对象–我们不能有两个标题相同的报表:

public class ReportService {private ReportRepository reportRepository;public ReportService(ReportRepository reportRepository) {this.reportRepository = reportRepository;}public void create(Title title, Content content) {if (!reportRepository.existsWithTitle(title)) {Report report = new Report(title, content);reportRepository.add(report);}}
}

我们可以通过多种方式测试此代码,但是我们将决定使用Fake Object:

class FakeReportRepository implements ReportRepository {private Map<Title, Report> reports = new HashMap<>();@Overridepublic void add(Report report) {reports.put(report.title(), report);}@Overridepublic boolean existsWithTitle(Title title) {return reports.containsKey(title);}@Overridepublic int countAll() {return reports.size();}@Overridepublic Report findByTitle(Title title) {return reports.get(title);}
}

我们的测试将如下所示:

public class FakeTest {@Testpublic void shouldNotCreateTheSameReportTwice() {FakeReportRepository reportRepository = new FakeReportRepository();ReportService reportService = aReportService(reportRepository);reportService.create(DUMMY_TITLE, DUMMY_CONTENT);reportService.create(DUMMY_TITLE, DUMMY_CONTENT);Report createdReport = reportRepository.findByTitle(DUMMY_TITLE);assertThat(createdReport.title()).isSameAs(DUMMY_TITLE);assertThat(createdReport.content()).isSameAs(DUMMY_CONTENT);assertThat(reportRepository.countAll()).isEqualTo(1);}private ReportService aReportService(ReportRepository reportRepository) {return new ReportService(reportRepository);}
}

存根对象

我们在对方法输出感兴趣的情况下使用Stub Object,以确保每次调用它时结果都将完全符合我们的期望。

通常,我们不会在测试中检查是否调用了Stub,因为我们会通过其他断言知道它。

在此示例中,我们将查看一个ReportFactory,该工厂将创建具有创建日期的报表。 为了可测试性,我们使用了依赖注入来注入DateProvider:

public class ReportFactory {private DateProvider dateProvider;public ReportFactory(DateProvider dateProvider) {this.dateProvider = dateProvider;}public Report crete(Title title, Content content) {return new Report(title, content, dateProvider.date());}
}

它允许我们在测试中使用Stub:

public class StubTest {@Testpublic void shouldCreateReportWithCreationDate() {Date dummyTodayDate = new Date();DateProvider dateProvider = mock(DateProvider.class);stub(dateProvider.date()).toReturn(dummyTodayDate);ReportFactory reportFactory = new ReportFactory(dateProvider);Report report = reportFactory.crete(DUMMY_TITLE, DUMMY_CONTENT);assertThat(report.creationDate()).isSameAs(dummyTodayDate);}
}

如您所见,我们仅对调用Stub的结果感兴趣。

间谍对象

与Stub对象相反,当我们对间谍方法的输入感兴趣时,我们将使用Spies。 我们正在检查它是否被调用。 我们可以检查它被调用了多少次。

我们也可以将实际的应用程序对象用作Spies。 无需创建任何其他类。

让我们从第一段回到ReportProcessor:

public class ReportProcessor implements Processor {// code@Overridepublic void process(Report report) {sender.send(report);}
}

可能您已经注意到我们在那里使用了Spy,但让我们再次看一下测试:

public class SpyTest {@Testpublic void shouldSentReportWhileProcessing() {Sender sender = spy(Sender.class);ReportProcessor reportProcessor = aReportProcessor(sender);reportProcessor.process(DUMMY_REPORT);then(sender).should().send(DUMMY_REPORT);}private ReportProcessor aReportProcessor(Sender sender) {return new ReportProcessor(sender);}
}

我们要检查对象是否以正确的方式包装,并将参数传递给其(包装的对象)方法调用。 这就是为什么我们在这里使用Spy。

模拟对象

模拟对象通常被描述为Stub和Spy的组合。 我们指定期望接收的输入,并在此基础上返回正确的结果。

如果这是我们期望的,则调用模拟对象也可能导致抛出异常。

好的,让我们再次看一下ReportService:

public class ReportService {//codepublic void create(Title title, Content content) {if (!reportRepository.existsWithTitle(title)) {Report report = new Report(title, content);reportRepository.add(report);}}
}

现在,我们将使用模拟对象代替伪对象:

@RunWith(MockitoJUnitRunner.class)
public class MockTest {@Mock private ReportRepository reportRepository;@InjectMocks private ReportService reportService;@Testpublic void shouldCreateReportIfDoesNotExist() {given(reportRepository.existsWithTitle(DUMMY_TITLE)).willReturn(false);reportService.create(DUMMY_TITLE, DUMMY_CONTENT);then(reportRepository).should().add(anyReport());}@Testpublic void shouldNotCreateReportIfDoesNotExist() {given(reportRepository.existsWithTitle(DUMMY_TITLE)).willReturn(true);reportService.create(DUMMY_TITLE, DUMMY_CONTENT);then(reportRepository).should(never()).add(anyReport());}private Report anyReport() {return any(Report.class);}
}

为了澄清一切,我们的模拟对象是ReportRepository.existsWithTitle()方法。 如您所见,在第一个测试中,我们说如果调用带有DUMMY_OBJECT参数的方法,它将返回true。 在第二个测试中,我们检查相反的情况。

我们在两个测试中的最后一个断言(then()。should())是另一个TD模式。 你能认出哪一个吗?

最后一句话

这就是我今天要说的有关测试双模式的全部内容。 我鼓励您有意使用它们,不要盲目遵循在可能的情况下添加@Mock注释的习惯。

我还邀请您阅读有关使用Test Double的后果的文章,以了解使用TD模式时可能遇到的问题以及如何识别和解决此类问题。

如果您想进一步加深对这些模式的了解,那么会有一个很棒的页面可以帮助您做到这一点: xUnit Patterns:Test Double 。

祝您测试顺利! 使它们可读且有价值。

如果您对“测试双模式”有任何想法或疑问,请在评论中分享。

翻译自: https://www.javacodegeeks.com/2015/09/test-double-patterns.html

双重for

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

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

相关文章

C++ STL详解(4)

点击蓝字关注我们来源于网络&#xff0c;侵删unordered_set / unordered_multiset这两个容器的方法与上一篇 C STL简介&#xff08;3&#xff09; 提到的 set / multiset 基本一样。但是要注意的是&#xff1a;这两个是无序的&#xff0c;基于哈希表实现的&#xff0c;增删改查…

js压缩图片_Web 性能优化: 图片优化让网站大小减少 62%

图像是web上提供的最基本的内容类型之一。他们说一张图片胜过千言万语。但是如果你不小心的话&#xff0c;图片大小有时高达几十兆。因此&#xff0c;虽然网络图像需要清晰明快&#xff0c;但它们尺寸可以缩小压缩的&#xff0c;使用加载时间保持在可接受的水平。在我的网站上&…

春春幼儿园堆积木大赛_春云边车

春春幼儿园堆积木大赛我有一个部署到基于NetflixOSS的云的应用程序&#xff0c;该应用程序具有以下结构&#xff1a; 本质上是一种将信息持久保存到Cassandra集群的服务。 所有应用程序都已注册到Eureka –因此&#xff0c;在本例中&#xff0c;该服务以及Cassandra节点都已在…

C++ 标准输入的行加速

点击蓝字关注我们来源于网络&#xff0c;侵删一、前言c/c 的标准输入函数&#xff0c;在输入一行数据&#xff0c;当数据量比较大的时候&#xff0c; cin 的效率是非常低的&#xff0c;为了加快读入的效率&#xff0c;可以改用 scanf&#xff0c;当然&#xff0c;还有一种利用 …

如何用python 开发web_如何用python开发web

Django是一个Web框架——一套用于帮助开发交互式网站的工具。Django能够响应网页请求&#xff0c;还能让你更轻松地读写数据库、管理用户等。建立Django项目建立Django项目首先需要确保我们按照好了Django&#xff0c;我们在Pycharm中打开虚拟环境的终端&#xff0c;然后输入&a…

redis安装_Redis安装

redis安装本文是我们学院课程的一部分&#xff0c;标题为Redis NoSQL键值存储 。 这是Redis的速成课程。 您将学习如何安装Redis和启动服务器。 此外&#xff0c;您还会在Redis命令行上乱七八糟。 接下来是更高级的主题&#xff0c;例如复制&#xff0c;分片和集群&#xff0c…

C++ 自动锁

点击蓝字关注我们来源于网络&#xff0c;侵删一、锁1.锁 是 多线程编程 中一个很常用的概念&#xff0c;这里不多加介绍其原理&#xff0c;有兴趣可以参考 临界区 进行更多的了解&#xff1b;2.锁 一般会提供三个接口&#xff1a;加锁&#xff08;Lock&#xff09;、解锁&#…

python快速编程入门黑马程序员pdf_Python快速编程入门 传智播客 黑马程序员 python编程从入门到实践基础视频教程核心编程爬虫数据分析程序设计机器学习简明书籍...

传智播客就业系列从入门到就业JAVA从入门到精通 套装5本&#xffe5;148javaweb从入门到精通套装3本&#xffe5;88Python从入门到实践套装8本&#xffe5;280php教程全套7本&#xffe5;227PHP套装5本&#xffe5;165php全套5本&#xffe5;149android开发从入门到精通 共3本&…

叉叉框架_叉/连接框架

叉叉框架本文是我们名为Java Concurrency Essentials的学院课程的一部分。 在本课程中&#xff0c;您将深入探讨并发的魔力。 将向您介绍并发和并发代码的基础知识&#xff0c;并学习诸如原子性&#xff0c;同步和线程安全性的概念。 在这里查看 &#xff01; 目录 1.简介 2…

C++ 浮点数精度判定

点击蓝字关注我们来源于网络&#xff0c;侵删一、引例看下下面这段代码&#xff0c;会输出什么结果呢&#xff1f;double x 0;for (int i 0; i < 10; i) {x 0.1;}printf("%d\n", x 1);输出如下&#xff1a;0引起这种反差的原因就是浮点误差&#xff0c;浮点数…

字节流和字符流哪个不刷新_不喜欢节流吗?

字节流和字符流哪个不刷新您别无选择–底层系统&#xff08;此处的JVM将为您完成选择&#xff09;。 我仍然记得2013年夏天&#xff0c;当时我正在运行一个项目&#xff0c;整个应用程序中只有1个URL使服务器瘫痪。 问题很简单-机器人决定以很高的速率索引我们的网站&#xff…

C/C++动态内存管理—(new与malloc)

点击蓝字关注我们来源于网络&#xff0c;侵删1.C/C内存分布虚拟地址空间分布&#xff1a;由C/C编译的程序占用的内存分为以下几个部分&#xff1a;栈区&#xff08;stack&#xff09;— 由编译器自动分配释放 &#xff0c;存放为运行函数而分配的局部变量、函数参数、返回数据、…

python实现简单小游戏_python实现简单井字棋小游戏

#Tic-Tac-Toe 井字棋游戏#全局常量X"X"O"O"EMPTY" "#询问是否继续def ask_yes_no(question):responseNone;while response not in("y","n"):responseinput(question).lower()return response#输入位置数字def ask_number(qu…

C++ sort()排序详解

点击蓝字关注我们来源自网络&#xff0c;侵删一.sort()简介1.为什么选择使用sort()我们经常会碰到排序的问题&#xff0c;如果我们不使用一些排序的方法那我们只能手撕排序&#xff0c;这样就会浪费一些时间。而且我们还需要根据需要去选择相关的排序方法&#xff1a;冒泡排序、…

java尾行注释有什么不好_注释不好吗?

java尾行注释有什么不好那天&#xff0c;我在有关Spring XML与注释的文章中运用了自己的原则&#xff0c;轻松进入了这个主题。 对于目前正在编写此新应用程序的团队来说&#xff0c;这种简单的输入方式也是我不使事情复杂化的方式&#xff0c;该应用程序的生产寿命可能为3-5年…

python输出结果为none_python的reverse函数翻转结果为None的问题

今天刷二级题的时候&#xff0c;遇到一个问题>>> L2[1,2,3,4]>>> L3L2.reverse()>>> print( L3)None>>> print(L3)None>>> print(L2.reverse())None其实我想让它输出[4,3,2,1]reverse函数&#xff0c;翻转列表然后我改了一下>…

性能测试流程_流性能

性能测试流程当我阅读Angelika Langer的Java性能教程时-Java 8流有多快&#xff1f; 我简直不敢相信&#xff0c;对于一个特定的操作&#xff0c;它们花费的时间比循环要长15倍。 流媒体性能真的会那么糟糕吗&#xff1f; 我必须找出答案&#xff01; 巧合的是&#xff0c;我最…

C++vector用法总结

点击蓝字关注我们来源自网络&#xff0c;侵删一.vector1. vector 说明1&#xff09;vector是C标准模板库中的部分内容&#xff0c;它是一个多功能的&#xff0c;能够操作多种数据结构和算法的模板类和函数库。2.&#xff09;vector之所以被认为是一个容器&#xff0c;是因为它能…

python中创建集合的语句_Python 集合(set) 介绍

集合 set集合是可变的容器集合内的数据对象都是唯一的(不能重复多次的)集合是无序的存储结构&#xff0c;集合中的数据没有先后关系集合内的元素必须是不可变对象集合是可迭代对象(可以用for等遍历)集合是相当于只有键&#xff0c;没有值的字典(键则是集合的数据)创建空集合&am…

C++ STL 线性容器的用法

点击蓝字关注我们来源于网络&#xff0c;侵删1.vectorvector 是顺序容器的一种&#xff0c;是可变长的动态数组&#xff0c;支持随机访问迭代器&#xff0c;所有stl算法都能对 vector 进行操作。vector 容器在实现时&#xff0c;动态分配的存储空间一般都大于存放元素所需的空间…