嘲弄和存根–了解Mockito的测试双打

介绍

我遇到的一件事是使用模拟框架的团队假设他们在模拟。
他们并不知道Mocks只是Gerard Meszaros在xunitpatterns.com上归类的“测试双打”之一。

重要的是要意识到每种类型的测试双精度在测试中都扮演着不同的角色。 用与您需要学习不同模式或重构的方式相同,您需要了解每种类型的测试double的原始角色。 然后可以将它们组合起来以满足您的测试需求。
我将简要介绍这种分类的产生方式以及每种类型的不同之处。
我将在Mockito中使用一些简短的简单示例进行此操作。

非常简短的历史

多年来,人们一直在编写系统组件的轻量级版本以帮助进行测试。 通常将其称为存根。 在2000年的文章“ Endo-Testing:使用模拟对象进行单元测试”中介绍了模拟对象的概念。 从那时起,Meszaros将存根,模拟和其他许多类型的测试对象归类为“测试双打”。
该术语已由Martin Fowler在“ Mocks Are n't Stubs”中引用,并在Microsoft社区中被采用,如“ Exploring The Test Doubles Continuum of Test Doubles”中所示。
参考部分中显示了指向这些重要论文的链接。

考试双打的类别

上图显示了常用的双重测试类型。 以下URL提供了对每个模式及其功能以及替代术语的很好的交叉引用。
http://xunitpatterns.com/Test%20Double.html

莫基托

Mockito是一个测试间谍框架,学习起来非常简单。 Mockito值得注意的是,在测试之前没有定义任何模拟对象的期望,因为它们有时在其他模拟框架中也是如此。 开始嘲笑时,这会导致更自然的样式(IMHO)。
以下示例在此处纯粹是为了简单演示如何使用Mockito实施不同类型的测试双打。

网站上有大量有关如何使用Mockito的特定示例。
http://docs.mockito.googlecode.com/hg/latest/org/mockito/Mockito.html

使用Mockito测试双打

以下是一些使用Mockito的基本示例,以显示Meszaros定义的每个测试双打的作用。
我为每个对象都提供了指向主要定义的链接,因此您可以获得更多示例和完整定义。

虚拟对象

http://xunitpatterns.com/Dummy%20Object.html
这是所有测试双打中最简单的一次。 这是一个没有实现的对象,仅用于填充与测试无关的方法调用的参数。

例如,下面的代码使用很多代码来创建客户,这对测试并不重要。
只要客户数恢复为1,该测试就不会在乎添加哪个客户。

public Customer createDummyCustomer() {County county = new County('Essex');City city = new City('Romford', county);Address address = new Address('1234 Bank Street', city);Customer customer = new Customer('john', 'dobie', address);return customer;
}@Test
public void addCustomerTest() {Customer dummy = createDummyCustomer();AddressBook addressBook = new AddressBook();addressBook.addCustomer(dummy);assertEquals(1, addressBook.getNumberOfCustomers());
}

我们实际上并不关心客户对象的内容,但是它是必需的。 我们可以尝试使用null值,但是如果代码正确,则可能会引发某种异常。

@Test(expected=Exception.class)
public void addNullCustomerTest() {Customer dummy = null;AddressBook addressBook = new AddressBook();addressBook.addCustomer(dummy);
}

为了避免这种情况,我们可以使用一个简单的Mockito假人来获得所需的行为。

@Test
public void addCustomerWithDummyTest() {Customer dummy = mock(Customer.class);AddressBook addressBook = new AddressBook();addressBook.addCustomer(dummy);Assert.assertEquals(1, addressBook.getNumberOfCustomers());
}

正是这个简单的代码创建了一个要传递给调用的虚拟对象。

Customer dummy = mock(Customer.class);

不要被模拟语法所迷惑-这里扮演的角色是虚拟的,而不是模拟的。
区别在于测试双重性的作用,而不是用于创建双重性的语法。

该类可以轻松替代客户类,并使测试非常容易阅读。

测试存根

http://xunitpatterns.com/Test%20Stub.html
测试存根的作用是将受控值返回到要测试的对象。 这些被描述为测试的间接输入。 希望有一个例子可以阐明这意味着什么。

采取以下代码

public class SimplePricingService implements PricingService
{ PricingRepository repository;public SimplePricingService(PricingRepository pricingRepository) {this.repository = pricingRepository;}@Overridepublic Price priceTrade(Trade trade) {return repository.getPriceForTrade(trade);}@Overridepublic Price getTotalPriceForTrades(Collectiontrades) {Price totalPrice = new Price();for (Trade trade : trades){Price tradePrice = repository.getPriceForTrade(trade);totalPrice = totalPrice.add(tradePrice);}return totalPrice;}

SimplePricingService具有一个协作对象,即交易存储库。 交易存储库通过getPriceForTrade方法将交易价格提供给定价服务。
为了测试SimplePricingService中的业务逻辑,我们需要控制这些间接输入
即我们从未通过测试的输入。 如下所示。

在以下示例中,我们对PricingRepository存根以返回可用于测试SimpleTradeService的业务逻辑的已知值。

@Test
public void testGetHighestPricedTrade() throws Exception {Price price1 = new Price(10); Price price2 = new Price(15);Price price3 = new Price(25);PricingRepository pricingRepository = mock(PricingRepository.class);when(pricingRepository.getPriceForTrade(any(Trade.class))).thenReturn(price1, price2, price3);PricingService service = new SimplePricingService(pricingRepository);Price highestPrice = service.getHighestPricedTrade(getTrades());assertEquals(price3.getAmount(), highestPrice.getAmount());
}

破坏者的例子

测试存根有两种常见的变体:响应者和破坏者。
如前面的示例,使用响应者来测试幸福的道路。
破坏者用于测试以下异常行为。

@Test(expected=TradeNotFoundException.class)
public void testInvalidTrade() throws Exception {Trade trade = new FixtureHelper().getTrade();TradeRepository tradeRepository = mock(TradeRepository.class);when(tradeRepository.getTradeById(anyLong())).thenThrow(new TradeNotFoundException());TradingService tradingService = new SimpleTradingService(tradeRepository);tradingService.getTradeById(trade.getId());
}

模拟对象

http://xunitpatterns.com/Mock%20Object.html
模拟对象用于在测试期间验证对象行为。 通过对象行为,我的意思是我们检查在运行测试时是否在对象上执行了正确的方法和路径。 这与存根的支持作用完全不同,存根用于为您要测试的任何内容提供结果。 在存根中,我们使用为方法定义返回值的模式。

when(customer.getSurname()).thenReturn(surname);

在模拟中,我们使用以下形式检查对象的行为。

verify(listMock).add(s);

这是一个简单的示例,我们要测试新交易是否已正确审核。
这是主要代码。

public class SimpleTradingService implements TradingService{TradeRepository tradeRepository;AuditService auditService;public SimpleTradingService(TradeRepository tradeRepository, AuditService auditService){this.tradeRepository = tradeRepository;this.auditService = auditService;}public Long createTrade(Trade trade) throws CreateTradeException {Long id = tradeRepository.createTrade(trade);auditService.logNewTrade(trade);return id;
}

以下测试为交易存储库创建存根,并为AuditService创建模拟
然后,我们在模拟的AuditService上调用verify,以确保TradeService调用了
logNewTrade方法正确

@Mock
TradeRepository tradeRepository;@Mock
AuditService auditService;@Test
public void testAuditLogEntryMadeForNewTrade() throws Exception { Trade trade = new Trade('Ref 1', 'Description 1');when(tradeRepository.createTrade(trade)).thenReturn(anyLong()); TradingService tradingService = new SimpleTradingService(tradeRepository, auditService);tradingService.createTrade(trade);verify(auditService).logNewTrade(trade);
}

以下行对模拟的AuditService进行检查。

verify(auditService).logNewTrade(trade);

该测试使我们能够证明审计服务在创建交易时的行为正确。

测试间谍

http://xunitpatterns.com/Test%20Spy.html
值得一看上面的链接,以严格定义测试间谍。
但是,在Mockito中,我喜欢使用它来包装实际对象,然后验证或修改其行为以支持您的测试。 这是一个示例,我们检查了列表的标准行为。 注意,我们既可以验证是否调用了add方法,也可以断言该项目已添加到列表中。

@Spy
List listSpy = new ArrayList();@Test
public void testSpyReturnsRealValues() throws Exception {String s = 'dobie';listSpy.add(new String(s));verify(listSpy).add(s);assertEquals(1, listSpy.size());
}

将其与仅可验证方法调用的模拟对象进行比较。 因为我们仅模拟列表的行为,所以它不记录已添加项目,并且在调用size()方法时返回默认值零。

@Mock
ListlistMock = new ArrayList();@Test
public void testMockReturnsZero() throws Exception {String s = 'dobie';listMock.add(new String(s));verify(listMock).add(s);assertEquals(0, listMock.size());
}

testSpy的另一个有用功能是能够对返回调用进行存根。 完成此操作后,该对象将正常运行,直到调用存根方法为止。
在此示例中,我们将get方法存根以始终引发RuntimeException。 其余行为保持不变。

@Test(expected=RuntimeException.class)
public void testSpyReturnsStubbedValues() throws Exception {listSpy.add(new String('dobie'));  assertEquals(1, listSpy.size());when(listSpy.get(anyInt())).thenThrow(new RuntimeException());listSpy.get(0);
}

在此示例中,我们再次保留了核心行为,但更改了size()方法,使其最初返回1,对所有后续调用返回5。

public void testSpyReturnsStubbedValues2() throws Exception {int size = 5;when(listSpy.size()).thenReturn(1, size);int mockedListSize = listSpy.size();assertEquals(1, mockedListSize);mockedListSize = listSpy.size();assertEquals(5, mockedListSize);  mockedListSize = listSpy.size();assertEquals(5, mockedListSize);  
}

这真是不可思议!

假物件

http://xunitpatterns.com/Fake%20Object.html
假物品通常是手工制作或重量较轻的物品,仅用于测试,不适合生产。 一个很好的例子是内存数据库或伪造的服务层。

它们往往提供比标准测试倍增功能更多的功能,因此通常不适合使用Mockito进行实现。 这并不是说它们不能像这样构造,仅仅是因为它可能不值得采用这种方式来实现。

参考:“ 小事半解 ” – 敏捷工程技术博客上来自我们JCG合作伙伴 John Dobie的Mockito了解测试双打 。


翻译自: https://www.javacodegeeks.com/2012/05/mocks-and-stubs-understanding-test.html

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

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

相关文章

【代码笔记】iOS-首页3张图片变化

一&#xff0c;效果图。 二&#xff0c;工程图。 三&#xff0c;代码。 RootViewController.h #import <UIKit/UIKit.h>interface RootViewController : UIViewController {NSTimer *timer;UIImageView *imageView1;UIImageView *imageView2;UIImageView *imageView3;UIV…

使用Eclipse在Amazon Ec2中部署Java Web应用程序的完整指南

嗨&#xff0c;读者们&#xff0c; 今天&#xff0c;我将向您展示如何使用Eclipse IDE在Amazon EC2中部署简单的Java Web应用程序。 在我们开始之前&#xff0c;我们需要一些必需的东西&#xff0c; Eclipse Java EE IDE –您可以从http://www.eclipse.org/downloads/下载&…

Spring 3和Java EE 6 –不公平和不完整的比较

这篇小文章的初稿标题为“ Spring&#xff06;Java EE –比较苹果和橙子”。 在撰写本文时&#xff0c;我了解到可以比较Spring Framework和Java EE&#xff0c;但这始终是不公平且不完整的工作。 Java for Enterprise和Spring Framework的发展紧密地联系在一起。 两者相互依存…

xml配置文件推荐方式

1.XML帮助类 /// <summary>/// Xml帮助类/// </summary>public class XmlHelper{/// <summary>/// 保存xml/// </summary>/// <typeparam name"T"></typeparam>/// <param name"path"></param>/// <p…

AFNetWorking https SSL认证

一般来讲如果app用了web service , 我们需要防止数据嗅探来保证数据安全.通常的做法是用ssl来连接以防止数据抓包和嗅探 其实这么做的话还是不够的 。 我们还需要防止中间人攻击&#xff08;不明白的自己去百度&#xff09;。攻击者通过伪造的ssl证书使app连接到了伪装的假冒的…

查看环境列表_Xfce 4.14桌面环境正式发布,想要图形界面又想节省内存?就它了...

1. Xfce 4.14桌面环境正式发布&#xff0c;它有什么新特性&#xff1f;本文主要讲解Xfce 4.14桌面环境正式发布&#xff0c;它有什么新特性。Xfce已经开发了4年多&#xff0c;但是这个周末终于看到了期待已久的Xfce 4.14的发布。Xfce 4.14是这个轻量级桌面环境的最新稳定版本&a…

卷积神经网络语音识别_用于物体识别的3D卷积神经网络

本文提出了一种基于CNN的3D物体识别方法&#xff0c;能够从3D图像表示中识别3D物体&#xff0c;并在比较了不同的体素时的准确性。已有文献中&#xff0c;3D CNN使用3D点云数据集或者RGBD图像来构建3D CNNs&#xff0c;但是CNN也可以用于直接识别物体体积表示的体素。本文中&am…

#获得请求来源ip_以太网数据包TCP、IP、ICMP、UDP、ARP协议头结构详解

以太网首部目地MAC地址(8字节)源MAC地址(8字节)类型(2字节)1、IP头的结构版本(4位)头长度(4位)服务类型(8位)封包总长度(16位)封包标识(16位)标志(3位)片断偏移地址(13位)存活时间(8位)协议(8位)校验和(16位)来源IP地址(32位)目的IP地址(32位)选项(可选)填充(可选)数据(1)字节和…

c# ef报错_C# EF调用MySql出现“未将对象引用设置到对象的实例”错误解决方案

C# EF调用MySql出现“未将对象引用设置到对象的实例”错误解决方案---修改步骤---1.打开Nuget管理包&#xff0c;把Mysql.Data替换为6.10.0以下任意版本。这里选择的是6.8.82.修改完毕后&#xff0c;继续把Mysql.Data.Entity也修改为对应版本6.8.8。3.安装完成后可以看到App.Co…

ServletRequest startAsync()的有用性有限

前段时间我遇到了Servlet 3.0中AsyncContext.start&#xff08;…&#xff09;的目的是什么&#xff1f; 题。 引用上述方法的Javadoc &#xff1a; 使容器调度线程&#xff08;可能从托管线程池中&#xff09;运行指定的Runnable 。 提醒大家&#xff0c; AsyncContext是Servl…

mysql所支持的比较运算符_mysql比较运算符有哪些?Mysql比较运算符详解

比较运算符可用于比较数字和字符串。今天发一篇Mysql比较运算符详解&#xff0c;希望对初学者有所帮助&#xff0c;虽然现在流行NoSQL&#xff0c;但是MYSQL还是很有用的&#xff0c;数字作为浮点值进行比较&#xff0c;字符串以不区为例进行比较&#xff0c;运算符用于比较表达…

Http Invoker的Spring Remoting支持

Spring HTTP Invoker是Java到Java远程处理的重要解决方案。 该技术使用标准的Java序列化机制通过HTTP公开服务&#xff0c;并且可以被视为替代解决方案&#xff0c;而不是Hessian和Burlap中的自定义序列化。 而且&#xff0c;它仅由Spring提供&#xff0c;因此客户端和服务器应…

学习后缀自动机想法

小序&#xff1a;学习后缀自动机是要有耐心的&#xff0c;clj的论文自己看真心酸爽&#xff01;&#xff08;还是自己太弱&#xff0c;ls&#xff0c;oyzx好劲啊&#xff0c;狂膜不止&#xff09; 刚刚在写博客之前又看了篇论文&#xff0c;终于看懂了&#xff0c;好开心 正文&…

mysql 分组top_MySQL:如何查询出每个分组中的 top n 条记录?

问题描述需求&#xff1a;查询出每月 order_amount(订单金额) 排行前3的记录。例如对于2019-02&#xff0c;查询结果中就应该是这3条&#xff1a;解决方法MySQL 5.7 和 MySQL 8.0 有不同的处理方法。1. MySQL 5.7我们先写一个查询语句。根据 order_date 中的年、月&#xff0c;…

利用jenkins的api来完成相关工作流程的自动化

[本文出自天外归云的博客园] 背景 1. 实际工作中涉及到安卓客户端方面的测试&#xff0c;外推或运营部门经常会有很多的渠道&#xff0c;而每个渠道都对应着一个app的下载包&#xff0c;这些渠道都记录在安卓项目下的一个渠道列表文件中。外推或运营部门经常会有新的渠道产生&a…

拥有成本分析:Oracle WebLogic Server与JBoss

Crimson Consulting Group 撰写的非常有趣的白皮书 &#xff0c;比较了Weblogic和JBoss之间的拥有成本 。 尽管JBoss是免费的&#xff0c;但该白皮书却严肃地宣称&#xff0c;从长远来看&#xff0c;Weblogic更便宜。 尽管此研究是由Oracle赞助的&#xff0c;但它看起来非常严肃…

mysql limit 分页 0_Mysql分页之limit用法与limit优化

Mysql limit分页语句用法与Oracle和MS SqlServer相比&#xff0c;mysql的分页方法简单的让人想哭。--语法&#xff1a;SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset--举例&#xff1a;select * from table limit 5; --返回前5行select * from table limit 0…

linux每天一小步---sed命令详解

1 命令功能 sed是一个相当强大的文件处理编辑工具&#xff0c;sed用来替换&#xff0c;删除&#xff0c;更新文件中的内容。sed以文本行为单位进行处理&#xff0c;一次处理一行内容。首先sed吧当前处理的行存储在临时的缓冲区中&#xff08;称为模式空间pattern space&#xf…

mysql trace工具_100% 展示 MySQL 语句执行的神器-Optimizer Trace

在上一篇文章《用Explain 命令分析 MySQL 的 SQL 执行》中&#xff0c;我们讲解了 Explain 命令的详细使用。但是它只能展示 SQL 语句的执行计划&#xff0c;无法展示为什么一些其他的执行计划未被选择&#xff0c;比如说明明有索引&#xff0c;但是为什么查询时未使用索引等。…

006_过滤器

过滤器 过滤器&#xff08;Filter&#xff09;把附加逻辑注入到MVC框的请求处理&#xff0c;实现了交叉关注。所谓交叉关注&#xff08;Cross-Cutting Concerns&#xff09;&#xff0c;是指可以用于整个应用程序&#xff0c;而又不适合放置在某个局部位置的功能&#xff0c;否…