- 浏览: 581507 次
- 性别:
- 来自: 广州
文章分类
- 全部博客 (188)
- java (14)
- web (14)
- web service (3)
- 杂谈 (14)
- Version Control (13)
- software test (30)
- linux (17)
- database (3)
- distributed storage and computing (1)
- ejb (7)
- project building (46)
- spring & IOC (2)
- Thread (2)
- xml (2)
- tool software (0)
- [网站分类]1.网站首页原创Java技术区(对首页文章的要求: 原创、高质量、经过认真思考并精心写作。BlogJava管理团队会对首页的文章进行管理。) (0)
- project manager (9)
- OSGI (1)
- nosql (3)
最新评论
-
sp42:
好搞笑
你懂不懂xml! (2) -
cherishmmo2004:
感觉你们都很牛掰,我们做的一个运维平台也是用karaf的,用k ...
基于osgi开发大型的企业应用 -
liubey:
“自作聪明”的使用了读写锁,其实只使用ReentrantLoc ...
编码最佳实践(4)--小心LinkedHashMap的get()方法 -
liubey:
你这个代码是sublist后仍然一直持有这个sub的引用,一般 ...
编码最佳实践(5)--小心!这只是冰山一角 -
xiegqooo:
初学maven(5)-使用assembly plugin实现自定义打包
作为测试的基本概念,在开发测试中经常遇到mock和stub。之前认为自己对这两个概念已经很明白了,但是当决定要写下来并写清楚以便能让不明白的人也能弄明白,似乎就很有困难。
试着写下此文,以检验自己是不是真的明白mock和stub。
一. 相同点
先看看两者的相同点吧,非常明确的是,mock和stub都可以用来对系统(或者将粒度放小为模块,单元)进行隔离。
在测试,尤其是单元测试中,我们通常关注的是主要测试对象的功能和行为,对于主要测试对象涉及到的次要对象尤其是一些依赖,我们仅仅关注主要测试对象和次要测试对象的交互,比如是否调用,何时调用,调用的参数,调用的次数和顺序等,以及返回的结果或发生的异常。但次要对象是如何执行这次调用的具体细节,我们并不关注,因此常见的技巧就是用mock对象或者stub对象来替代真实的次要对象,模拟真实场景来进行对主要测试对象的测试工作。
因此从实现上看,mock和stub都是通过创建自己的对象来替代次要测试对象,然后按照测试的需要控制这个对象的行为。
二. 不同点
1. 类实现的方式
从类的实现方式上看,stub有一个显式的类实现,按照stub类的复用层次可以实现为普通类(被多个测试案例复用),内部类(被同一个测试案例的多个测试方法复用)乃至内部匿名类(只用于当前测试方法)。对于stub的方法也会有具体的实现,哪怕简单到只有一个简单的return语句。
而mock则不同,mock的实现类通常是有mock的工具包如easymock, jmock来隐式实现,具体mock的方法的行为则通过record方式来指定。
以mock一个UserService, UserDao为例,最简单的例子,只有一个查询方法:
stub的标准实现,需要自己实现一个类并实现方法:
mock的实现,以easymock为例,只要指定mock的类并record期望的行为,并没有显式的构造新类:
对比可以看出,mock编写相对简单,只需要关注被使用的函数,所谓"just enough"。stub要复杂一些,需要实现逻辑,即使是不需要关注的方法也至少要给出空实现。
2. 测试逻辑的可读性
从上面的代码可以看出,在形式上,mock通常是在测试代码中直接mock类和定义mock方法的行为,测试代码和mock的代码通常是放在一起的,因此测试代码的逻辑也容易从测试案例的代码上看出来。Easymock.expect(dao.getById("1001")).andReturn(user); 直截了当的指明了当前测试案例对UserDao这个依赖的预期: getById需要被调用,调用的参数应该是"1001",调用次数为1(不明确指定调用次数时easymock默认为1)。
而stub的测试案例的代码中只有简单的UserDao userDao = new UserDaoStub ();构造语句和service.setUserDao(userDao);设置语句,我们无法直接从测试案例的代码中看出对依赖的预期,只能进入具体的UserServiceImpl类的query()方法,看到具体的实现是调用userDao.getById(userId),这个时候才能明白完整的测试逻辑。因此当测试逻辑复杂,stub数量多并且某些stub需要传入一些标记比如true,false之类的来制定不同的行为时,测试逻辑的可读性就会下降。
3. 可复用性
Mock通常很少考虑复用,每个mock对象通过都是遵循"just enough"原则,一般只适用于当前测试方法。因此每个测试方法都必须实现自己的mock逻辑,当然在同一个测试类中还是可以有一些简单的初始化逻辑可以复用。
stub则通常比较方便复用,尤其是一些通用的stub,比如jdbc连接之类。spring框架就为此提供了大量的stub来方便测试,不过很遗憾的是,它的名字用错了:spring-mock!
4. 设计和使用
接着我们从mock和stub的设计和使用上来比较两者,这里需要引入两个概念:interaction-based和state-based。
具体关于interaction-based和state-based,不再本文阐述,强烈推荐Martin Fowler 的一篇文章,"Mocks Aren't Stubs"。地址为http://martinfowler.com/articles/mocksArentStubs.html(PS:当在google中输入mock stub两个关键字做搜索时,出来结果的第一条就是此文,向Martin Fowler致敬,向google致敬),英文不好的同学,可以参考这里的一份中文翻译:http://www.cnblogs.com/anf/archive/2006/03/27/360248.html。
总结来说,stub是state-based,关注的是输入和输出。mock是interaction-based,关注的是交互过程。
5. expectiation/期望
这个才是mock和stub的最重要的区别:expectiation/期望。
对于mock来说,exception是重中之重:我们期待方法有没有被调用,期待适当的参数,期待调用的次数,甚至期待多个mock之间的调用顺序。所有的一切期待都是事先准备好,在测试过程中和测试结束后验证是否和预期的一致。
而对于stub,通常都不会关注exception,就像上面给出的UserDaoStub的例子,没有任何代码来帮助判断这个stub类是否被调用。虽然理论上某些stub实现也可以通过自己编码的方式增加对expectiation的内容,比如增加一个计数器,每次调用+1之类,但是实际上极少这样做。
6. 总结
关于mock和stub的不同,在Martin Fowler的"Mocks Aren't Stubs"一文中,有以下结束,我将它列出来作为总结:
(1) Dummy
对象被四处传递,但是从不被真正使用。通常他们只是用来填充参数列表。
(2) Fake
有实际可工作的实现,但是通常有一些缺点导致不适合用于产品(基于内存的数据库就是一个好例子)。
(3) Stubs
在测试过程中产生的调用提供预备好的应答,通常不应答计划之外的任何事。stubs可能记录关于调用的信息,比如 邮件网关的stub 会记录它发送的消息,或者可能仅仅是发送了多少信息。
(4) Mocks
如我们在这里说的那样:预先计划好的对象,带有各种期待,他们组成了一个关于他们期待接受的调用的详细说明。
三. 退化和转化
在实际的开发测试过程中,我们会发现其实mock和stub的界限有时候很模糊,并没有严格的划分方式,从而造成我们理解上的含糊和困惑。
主要的原因在于现实使用中,我们经常将mock做不同程度的退化,从而使得mock对象在某些程度上如stub一样工作。以easymock为例,我们可以通过anyObject(), isA(Class)等方式放宽对参数的检测,以atLeastOnce(),anytimes()来放松对调用次数的检测,我们可以使用Easymock.createControl()而不是Easymock.createStrictControl()来放宽对调用顺序的检测(或者调用checkOrder(false)),我们甚至可以通过createNiceControl(), createNiceMock()来创建完全不限制调用方式而且自动返回简单值的mock,这和stub就几乎没有本质区别了。
目前大多数的mock工具都提供mock退化为stub的支持,比如easyock中,除了上面列出的any***,NiceMock之外,还提供诸如andStubAnswer(),andStubDelegateTo(),andStubReturn(),andStubThrow()和asStub()。
前面也谈到过stub也是可以通过增加代码来实现一些expectiation的特性,stub理论上也是可以向mock的方向做转化,而从使得两者的界限更加的模糊。
感谢指正,已经修改。
试着写下此文,以检验自己是不是真的明白mock和stub。
一. 相同点
先看看两者的相同点吧,非常明确的是,mock和stub都可以用来对系统(或者将粒度放小为模块,单元)进行隔离。
在测试,尤其是单元测试中,我们通常关注的是主要测试对象的功能和行为,对于主要测试对象涉及到的次要对象尤其是一些依赖,我们仅仅关注主要测试对象和次要测试对象的交互,比如是否调用,何时调用,调用的参数,调用的次数和顺序等,以及返回的结果或发生的异常。但次要对象是如何执行这次调用的具体细节,我们并不关注,因此常见的技巧就是用mock对象或者stub对象来替代真实的次要对象,模拟真实场景来进行对主要测试对象的测试工作。
因此从实现上看,mock和stub都是通过创建自己的对象来替代次要测试对象,然后按照测试的需要控制这个对象的行为。
二. 不同点
1. 类实现的方式
从类的实现方式上看,stub有一个显式的类实现,按照stub类的复用层次可以实现为普通类(被多个测试案例复用),内部类(被同一个测试案例的多个测试方法复用)乃至内部匿名类(只用于当前测试方法)。对于stub的方法也会有具体的实现,哪怕简单到只有一个简单的return语句。
而mock则不同,mock的实现类通常是有mock的工具包如easymock, jmock来隐式实现,具体mock的方法的行为则通过record方式来指定。
以mock一个UserService, UserDao为例,最简单的例子,只有一个查询方法:
public interface UserService { User query(String userId); } public class UserServiceImpl implements UserService { private UserDao userDao; public User query(String userId) { return userDao.getById(userId); } //setter for userDao } public interface UserDao { User getById(String userId); }
stub的标准实现,需要自己实现一个类并实现方法:
public class UserDaoStub implements UserDao { public User getById(String id) { User user = new User(); user.set..... return user; } } @Test public void testGetById() { UserServiceImpl service = new UserServiceImpl(); UserDao userDao = new UserDaoStub(); service.setUserDao(userDao); User user = service.query("1001"); ... }
mock的实现,以easymock为例,只要指定mock的类并record期望的行为,并没有显式的构造新类:
@Test public void testGetById() { UserDao dao = Easymock.createMock(UserDao.class); User user = new User(); user.set..... Easymock.expect(dao.getById("1001")).andReturn(user); Easymock.reply(dao); UserServiceImpl service = new UserServiceImpl(); service.setUserDao(userDao); User user = service.query("1001"); ... Easymock.verify(dao); }
对比可以看出,mock编写相对简单,只需要关注被使用的函数,所谓"just enough"。stub要复杂一些,需要实现逻辑,即使是不需要关注的方法也至少要给出空实现。
2. 测试逻辑的可读性
从上面的代码可以看出,在形式上,mock通常是在测试代码中直接mock类和定义mock方法的行为,测试代码和mock的代码通常是放在一起的,因此测试代码的逻辑也容易从测试案例的代码上看出来。Easymock.expect(dao.getById("1001")).andReturn(user); 直截了当的指明了当前测试案例对UserDao这个依赖的预期: getById需要被调用,调用的参数应该是"1001",调用次数为1(不明确指定调用次数时easymock默认为1)。
而stub的测试案例的代码中只有简单的UserDao userDao = new UserDaoStub ();构造语句和service.setUserDao(userDao);设置语句,我们无法直接从测试案例的代码中看出对依赖的预期,只能进入具体的UserServiceImpl类的query()方法,看到具体的实现是调用userDao.getById(userId),这个时候才能明白完整的测试逻辑。因此当测试逻辑复杂,stub数量多并且某些stub需要传入一些标记比如true,false之类的来制定不同的行为时,测试逻辑的可读性就会下降。
3. 可复用性
Mock通常很少考虑复用,每个mock对象通过都是遵循"just enough"原则,一般只适用于当前测试方法。因此每个测试方法都必须实现自己的mock逻辑,当然在同一个测试类中还是可以有一些简单的初始化逻辑可以复用。
stub则通常比较方便复用,尤其是一些通用的stub,比如jdbc连接之类。spring框架就为此提供了大量的stub来方便测试,不过很遗憾的是,它的名字用错了:spring-mock!
4. 设计和使用
接着我们从mock和stub的设计和使用上来比较两者,这里需要引入两个概念:interaction-based和state-based。
具体关于interaction-based和state-based,不再本文阐述,强烈推荐Martin Fowler 的一篇文章,"Mocks Aren't Stubs"。地址为http://martinfowler.com/articles/mocksArentStubs.html(PS:当在google中输入mock stub两个关键字做搜索时,出来结果的第一条就是此文,向Martin Fowler致敬,向google致敬),英文不好的同学,可以参考这里的一份中文翻译:http://www.cnblogs.com/anf/archive/2006/03/27/360248.html。
总结来说,stub是state-based,关注的是输入和输出。mock是interaction-based,关注的是交互过程。
5. expectiation/期望
这个才是mock和stub的最重要的区别:expectiation/期望。
对于mock来说,exception是重中之重:我们期待方法有没有被调用,期待适当的参数,期待调用的次数,甚至期待多个mock之间的调用顺序。所有的一切期待都是事先准备好,在测试过程中和测试结束后验证是否和预期的一致。
而对于stub,通常都不会关注exception,就像上面给出的UserDaoStub的例子,没有任何代码来帮助判断这个stub类是否被调用。虽然理论上某些stub实现也可以通过自己编码的方式增加对expectiation的内容,比如增加一个计数器,每次调用+1之类,但是实际上极少这样做。
6. 总结
关于mock和stub的不同,在Martin Fowler的"Mocks Aren't Stubs"一文中,有以下结束,我将它列出来作为总结:
(1) Dummy
对象被四处传递,但是从不被真正使用。通常他们只是用来填充参数列表。
(2) Fake
有实际可工作的实现,但是通常有一些缺点导致不适合用于产品(基于内存的数据库就是一个好例子)。
(3) Stubs
在测试过程中产生的调用提供预备好的应答,通常不应答计划之外的任何事。stubs可能记录关于调用的信息,比如 邮件网关的stub 会记录它发送的消息,或者可能仅仅是发送了多少信息。
(4) Mocks
如我们在这里说的那样:预先计划好的对象,带有各种期待,他们组成了一个关于他们期待接受的调用的详细说明。
三. 退化和转化
在实际的开发测试过程中,我们会发现其实mock和stub的界限有时候很模糊,并没有严格的划分方式,从而造成我们理解上的含糊和困惑。
主要的原因在于现实使用中,我们经常将mock做不同程度的退化,从而使得mock对象在某些程度上如stub一样工作。以easymock为例,我们可以通过anyObject(), isA(Class)等方式放宽对参数的检测,以atLeastOnce(),anytimes()来放松对调用次数的检测,我们可以使用Easymock.createControl()而不是Easymock.createStrictControl()来放宽对调用顺序的检测(或者调用checkOrder(false)),我们甚至可以通过createNiceControl(), createNiceMock()来创建完全不限制调用方式而且自动返回简单值的mock,这和stub就几乎没有本质区别了。
目前大多数的mock工具都提供mock退化为stub的支持,比如easyock中,除了上面列出的any***,NiceMock之外,还提供诸如andStubAnswer(),andStubDelegateTo(),andStubReturn(),andStubThrow()和asStub()。
前面也谈到过stub也是可以通过增加代码来实现一些expectiation的特性,stub理论上也是可以向mock的方向做转化,而从使得两者的界限更加的模糊。
评论
5 楼
dotjar
2011-12-02
不太看得明白,有点费劲,
4 楼
fengjia10
2010-08-31
单纯从概念上来看,很难说清楚两者的不同。在分布式计算中,stub代表的是存根,即客户端代理,从测试理论上讲,stub代表的是桩模块,也就是上层模块所调用的模块。而mock的含义大家应该都很清楚哈,Java世界优秀的框架jmock,easymock,jmockit都充斥着以此开头或结尾的方法,其根本是用来隐藏掉你所依赖模块的细节,直接伪造一个(还是代理嗷)。
不管怎么说,楼主的分享精神还是要赞一个的!
不管怎么说,楼主的分享精神还是要赞一个的!
3 楼
skydream
2010-08-31
wjn811 写道
atLeatOnce() -> atLeastOnce()
感谢指正,已经修改。
2 楼
wjn811
2010-08-31
atLeatOnce() -> atLeastOnce()
1 楼
skydream
2010-08-30
很惊讶的发现,获得了11个精华帖投票,但是居然没有一个回复!
无论如何,感谢投票的弟兄们!
无论如何,感谢投票的弟兄们!
发表评论
-
easymock教程-运行时返回值或者异常
2010-11-30 16:36 5692前面的教程中,我们看到easymock可以通过 ... -
easymock教程-改变同一个方法调用的行为
2010-11-30 17:06 3333在easymock中,对于mock对象的同一个方法,可 ... -
easymock教程-自定义参数匹配器
2010-11-30 18:18 4050虽然easymock中提供了大量的方法来进行参数匹配, ... -
easymock教程-目录
2010-10-14 10:44 8440easymock是目前比较流行的java mock 工 ... -
easymock教程-单元测试中的主要测试对象和依赖
2010-10-14 14:01 3636在单元测试中,通常我们都会有一个明确的测试对象,我们 ... -
easymock教程-record-replay-verify模型
2010-10-15 14:50 7275record-replay-verify 模型容许记录 ... -
easymock教程-easymock的典型使用
2010-10-15 17:14 6183关于easymock的典型使用方式,在easym ... -
easymock教程-class mocking
2010-10-26 16:54 2793前面的例子中,mock的对象都是基于interface ... -
easymock教程-使用MockControl
2010-10-26 17:18 4432在easymock中,通常我们使用一下的代码来创建m ... -
easymock教程-strict和nice
2010-11-19 11:39 4132在easymock的使用过程中,当创建mock对象时, ... -
easymock教程-创建stub对象
2010-11-23 17:51 3149前面教程中有个章节讨论到mock和stub的概念差 ... -
easymock教程-mock的限制
2010-11-25 11:12 12210easymock并不是万能的,在使用easymock时 ... -
easymock教程-放宽调用次数
2010-11-29 15:55 3245对于mock对象上的mock方法的调用,easymoc ... -
easymock教程-命名mock对象
2010-11-29 16:34 3035在创建mock对象的时候,我们可以命名mock对象。 ... -
easymock教程-参数匹配
2010-11-29 18:57 7191easymock中提供了非常多的方法来实现参数匹配,基 ... -
easymock教程-partial class mocking
2010-11-30 14:23 2875easymock中提供对于类的mock功能,我们可以方 ... -
推荐升级easymock到新的3.0版本
2010-06-26 20:40 1834一直在使用easymock作为mock工具,但是eas ... -
loadrunner license设置问题
2008-02-18 11:35 7448初学loadrunner,今天准备用这个工具测试一下手头的一个 ... -
TestNG官方文档中文版(1)-介绍
2008-03-18 22:25 1771最近决定开始使用TestNG ... -
TestNG官方文档中文版(2)-annotation
2008-03-19 23:22 1885TestNG的官方文档的中文翻译版第二章,原文请见 http: ...
相关推荐
#### 1.2 easymock教程-mock和stub ##### 一、相同点 mock和stub在测试领域中都是为了实现对测试对象(通常是待测代码)与其他系统组件之间交互的模拟,从而确保测试环境的纯净性。它们的主要作用在于: - **隔离...
easymock教程 easymock是一种流行的mocking框架,用于单元测试中模拟依赖对象的行为。下面是关于easymock的详细知识点: ### easymock的基本概念 * Mock对象:是一种虚拟对象,模拟真实对象的行为,用于单元测试...
- **相同点**:Mock与Stub都用于实现系统隔离,即在测试过程中,用它们来替代次要对象(即依赖项),使得测试能够集中关注于主要测试对象的功能和行为。 - **不同点**: - **类实现方式**:Stub具有一个显式的类...
本教程详细介绍了Easymock的各种特性和使用方法,旨在帮助软件开发人员掌握这一工具。 1. **Mock和Stub的概念** - Mock对象是用于代替真实对象的虚拟对象,它在测试中按照预设的规则来响应调用,帮助验证方法是否...
1. **Mock和Stub**: Mock对象是模拟了特定行为的对象,用于验证方法是否被正确调用,以及它们被调用的方式。Stub则是预先定义了返回值或行为的对象,当被调用时,它们按照预设的规则进行响应,而不进行任何验证。 ...
2. **Mock和Stub的区别**:Mock对象会在测试后验证它被调用的方式,而Stub对象只返回预定义的值,不进行任何验证。 3. **测试DAO**:讲解如何使用Easymock模拟数据库操作,比如预设查询、插入、更新或删除的方法...
Java单元测试对于开发人员质量保证至关重要,尤其当...Mock说白了就是打桩(Stub)或则模拟,当你调用一个不好在测试中创建的对象时,Mock框架为你模拟一个和真实对象类似的替身来完成相应的行为。使用如下方式在Mav
2. **Mocking 和 Stubbing**:了解如何使用 Mock 对象替代实际依赖,以及如何设置 Stub 方法来控制预期的行为。 3. **PowerMock 功能**:学习如何使用 PowerMock 来模拟静态方法、构造函数、final 类和方法、私有...
9. **假对象(Mocks)与桩(Stubs)**:在单元测试中,有时需要模拟(mock)或替身(stub)外部依赖,JUnit 本身不提供这个功能,但可以结合使用 Mockito 或 EasyMock 这样的库来实现。 10. **测试驱动开发(TDD)*...
3. **Mocking和Stubbing**:在单元测试中,有时需要模拟(mock)或存根(stub)依赖对象的行为,以隔离测试。Java社区有Mockito、EasyMock和PowerMock等工具来实现这一目的。 4. **集成测试**:对于涉及数据库、...
7. **假对象和Mocking**:在JUnit测试中,经常需要模拟(mock)或桩(stub)其他依赖对象,以隔离被测试的代码。JUnit本身并不提供模拟功能,但可以与其他库如Mockito或EasyMock结合使用。 8. **测试驱动开发(TDD...
3. **Mocking和Stubbing**: 在复杂的系统中,有时需要模拟(mock)或存根(stub)依赖对象的行为,以隔离测试并专注于待测试代码的功能。Java社区有多个库可用于此目的,如Mockito和EasyMock。通过mocking,我们可以...