Stub和Mock的理解
我对Stub和Mock的理解
介绍
使用测试驱动开发大半年了,我还是对Stub和Mock的认识比较模糊,没有进行系统整理。
今天查阅了相关资料,觉得写得很不错,所以我试图在博文中对资料进行整理一下,再加上一些自己的观点。
本文是目前我对Stub和Mock的认识,难免有偏差,欢迎大家拍砖。
分析
Stub和Mock都是属于测试替身,对类型细分的话可以分为:
- Dummy Object
- Fake Object
- Test Stub
- Test Spy
- Mock Object
前四项属于Stub,最后的Mock Object属于Mock。
类型分析
Dummy Object(哑对象)
测试代码仅仅是需要使用它来通过编译,实际上用不到它。如测试A类的run方法,需要在创建A类的实例时需要传入B类实例,但run方法并没有用到B类实例。在测试时需要传入B类的哑对象new NullB()(如“new A(new NullB())”),让其通过编译。这里的NullB是一个空类,没有具体实现。
Fake Object(假对象)
假对象相对于哑对象来说,要对耦合的组件有一些简单的实现,实现我们在测试中要用到的方法,指定期望的行为(如返回期望的值)。假对象适用于替换产品代码中使用的全局对象,或者创建的类。这里注意的是要先对被替换的全局对象或类进行备份,然后在测试完成后进行恢复。
示例1(替换全局对象):

//产品代码
function A(){
this.num = 0;
}
A.prototype.run = function(){
this.num = window.b.getNum();
}; //测试代码
describe("测试A类的run方法", function(){
var temp = null; function backUp(){
window.b = window.b || {};
temp = YYC.Tool.extendDeep(window.b);
}
function restore(){
window.b = temp;
} beforeEach(function(){
backUp();
});
afterEach(function(){
restore();
}); it("获得数字", function () {
window.b = { //假对象
getNum: function(){
return 1;
}
} var a = new A();
a.run(); expect(a.num).toEqual(1);
});
});

示例2(替换类):

//产品代码
function A() {
this.num = 0;
this._b = new B();
}
A.prototype.run = function () {
this.num = this._b.getNum();
}; //测试代码
describe("测试A类的run方法", function () {
var temp = null; function backUp() {
window.B = window.B || function () {};
temp = B;
} function restore() {
window.B = temp;
} beforeEach(function () {
backUp();
});
afterEach(function () {
restore();
}); it("获得数字", function () {
window.B = function () {
};
window.B.prototype.getNum = function () {
return 1;
}; var a = new A();
a.run(); expect(a.num).toEqual(1);
});
});

Test Stub(测试桩)
测试桩与假对象有点类似,也要实现与产品代码耦合的组件,指定期望的行为。这里最大的不同是测试桩需要注入到产品代码中,从而在测试产品代码时替换组件,执行桩的行为。使用测试桩不需要进行备份和还原。
示例:

//产品代码
function A(b) {
this.num = 0;
this._b = b;
}
A.prototype.run = function () {
this.num = this._b.getNum();
}; //测试代码
describe("测试A类的run方法", function () {
it("获得数字", function () {
var stub_B = { //B类的桩
getNum: function(){
return 1;
}
}; var a = new A(stub_B); //注入桩
a.run(); expect(a.num).toEqual(1);
});
});

Test Spy(嗅探桩)
与测试桩类似,但是可以记录桩使用的记录,并进行验证。
示例:
可以使用jasmine的spy来举例。

//产品代码
function A(b) {
this.num = 0;
this._b = b;
}
A.prototype.run = function () {
this.num = this._b.getNum();
}; //测试代码
describe("测试A类的run方法", function () {
it("获得数字", function () {
var stub_b = {
getNum: function(){
return 1;
}
};
spyOn(stub_b, "getNum").andCallThrough(); //嗅探桩的getNum方法 var a = new A(stub_b); //注入桩
a.run(); expect(a.num).toEqual(1);
expect(stub_b.getNum).toHaveBeenCalled(); //验证调用过桩的getNum方法
});
});

Mock Object(模拟对象)
设定产品代码中耦合的类的期望的行为,然后验证期望的行为是否发生,从而达到测试产品代码行为的目的。适用于验证一些void的行为。例如:在某个条件发生时,要记录Log。这种情景,用stub就很难验证,因为对目标物件来说,沒有回传值,也沒有状态变化,就只能通过mock object來验证目标物件是否正确的与Log介面进行互动。
示例:

//产品代码
function A(b) {
this.num = 0;
this._b = b;
}
A.prototype.run = function () {
this.num = this._b.getNum(2);
}; //测试代码
describe("测试A类的run方法", function () {
it("获得数字", function () {
var mockB = Mock.createMock({
getNum: function(){}
}); //如果B类存在的话,也可以直接传入B的原型:var mockB = Mock.createMock(B.prototype);
Mock.expect(mockB.getNum, 2).return(1).times(1); var a = new A(mockB);
a.run(); expect(a.num).toEqual(1);
Mock.verify(); //验证期望的行为发生:mockB的getNum传入的参数为2;调用了1次mockB.getNum
});
});

Mock(Mock Object)与Spy(Test Spy)的比较
相同点
- 都要注入到产品代码中。
不同的
- Mock是替换整个被Mock的类,这个类可以存在也可以不存在。而Spy是使用一个已经存在的类,嗅探其中的部分方法。
- 从流程中来说,Mock是先设定被Mock的类的期望行为,然后验证期望的行为是否发生。Spy是记录下桩的方法的使用记录(如传入的参数,调用的次数等),然后再对记录进行验证。
Mock退化为Stub

//产品代码
function A(b) {
this.num = 0;
this._b = b;
}
A.prototype.run = function () {
this.num = this._b.getNum(2);
}; //测试代码
describe("测试A类的run方法", function () {
it("获得数字", function () {
var mockB = Mock.createMock({
getNum: function(){}
}); //如果B类存在的话,也可以直接传入B的原型:var mockB = Mock.createMock(B.prototype);
Mock.expect(mockB.getNum).return(1); //只指定返回值,没有期望的参数或期望调用的次数。因此不用verify来验证了! var a = new A(mockB);
a.run(); expect(a.num).toEqual(1);
});
});

也可以用Stub来达到相同的效果:

//产品代码
function A(b) {
this.num = 0;
this._b = b;
}
A.prototype.run = function () {
this.num = this._b.getNum();
}; //测试代码
describe("测试A类的run方法", function () {
it("获得数字", function () {
var stub_B = {
getNum: function(){
return 1;
}
}; var a = new A(stub_B);
a.run(); expect(a.num).toEqual(1);
});
});

总结
在比较简单的情况下(如需要哑对象来通过编译,或是需要测试桩来替换耦合的组件),使用Stub。
如果需要验证耦合组件的行为,可以使用Spy或Mock。
参考资料
30天快速上手 TDD Day 7 - Unit Test - Stub, Mock, Fake 簡介
《xUnit测试模式--测试码重构》
Stub和Mock的理解的更多相关文章
- 我对Stub和Mock的理解
介绍 使用测试驱动开发大半年了,我还是对Stub和Mock的认识比较模糊,没有进行系统整理. 今天查阅了相关资料,觉得写得很不错,所以我试图在博文中对资料进行整理一下,再加上一些自己的观点. 本文是目 ...
- [转]TDD之Dummy Stub Fake Mock
TDD之Dummy Stub Fake Mock 测试驱动大家都很熟悉了,这两天正好看了一个java的书,对TDD中的一些基本概念进行了复习,具体如下: Dummy An object that is ...
- What's the difference between a stub and mock?
I believe the biggest distinction is that a stub you have already written with predetermined behavio ...
- TDD 中关于mock一些理解
最近在写代码的UT时case注意到: 在写某个类的test suit时,如果这个类既有组合(Composition),又有聚合关系(Aggregation). 组合关系(Composition):部分 ...
- 从0开发3D引擎(四):搭建测试环境
目录 上一篇博文 了解自动化测试 单元测试 集成测试 端对端测试 通过打印日志来调试 了解运行测试 断点调试 通过Spector.js测试WebGL 通过log调试Shader 移动端测试 了解性能测 ...
- [转]软件测试- 3 - Mock 和Stub的区别
由于一直没有完全搞明白Mock和Stub的区别,所以查了很多文章,而这一篇是做好的: http://yuan.iteye.com/blog/470418 尤其是8楼,Frostred的发言,描述地相当 ...
- 置换测试: Mock, Stub 和其他
简介 在理想情况下,你所做的所有测试都是能应对你实际代码的高级测试.例如,UI 测试将模拟实际的用户输入(Klaas 在他的文章中有讨论)等等.实但际上,这并非永远都是个好主意.为每个测试用例都访问一 ...
- TDD:什么是桩(stub)和模拟(mock)?
背景 本文假设你对TDD.Stub和Mock已经有了初步的认识,本文简单解释一下stub和mock的区别和使用场景,先看看他们之间的关系: 由上图可以知道mock框架可以非常容易的开发stub和moc ...
- TDD学习笔记【六】一Unit Test - Stub, Mock, Fake 简介
这篇文章简介一下,如何通过 mock framework,来辅助我们更便利地模拟目标对象的依赖对象,而不必手工敲堆只为了这次测试而存在的辅助类型. 而模拟目标对象的部分,常见的有 stub objec ...
随机推荐
- 在LINQ中实现多条件联合主键LEFT JOIN
我昨天遇到一个LINQ下使用多条件比对产生LEFT JOIN的问题,经过深入研究,终于解决了,也让我学到了新的东西,特地拿来分享. 实例:有一张库存异常变更视图KCYD,仓库ID[Ckid]和物品ID ...
- ASP.NET 5- 1
ASP.NET 5 入门(1) - 建立和开发ASP.NET 5 项目 ASP.NET入门(1) - 建立和开发ASP.NET 5 项目 建立项目 首先,目前只有VS 2015支持开发最新的ASP ...
- Redis 安装与简单示例
Redis 安装与简单示例 一.Redis的安装 Redis下载地址如下:https://github.com/dmajkic/redis/downloads 解压后根据自己机器的实际情况选择32位或 ...
- Java用ZIP格式压缩和解压缩文件
转载:java jdk实例宝典 感觉讲的非常好就转载在这保存! java.util.zip包实现了Zip格式相关的类库,使用格式zip格式压缩和解压缩文件的时候,须要导入该包. 使用zipoutput ...
- hdu 4932 Miaomiao's Geometry(暴力)
题目链接:hdu 4932 Miaomiao's Geometry 题目大意:在x坐标上又若干个点,如今要用若干条相等长度的线段覆盖这些点,若一个点被一条线段覆盖,则必须在这条线的左端点或者是右端点, ...
- asp.net 给按钮 增加事件
一个页面,有查询,审核,删除,取消审核 按钮,每次结尾 处都要 调用 Initdata方法,重新刷新数据 繁琐哇,我的解决方法是 protected void Page_Load(object sen ...
- <C++ 实现设计模式> 观察者模式
观察者模式,又称公布--订阅,mvc模式等. 通俗点讲,比方股票来说,非常多人关注一支股票,派一个人去观察股票的情况,一有变化(观察),就通知全部的预定这个消息的人. 而我们常见的mvc模式,v是指v ...
- 湘潭oj1203/邀请赛A称号 数论+java睑板
乞讨 n%1+n%2+n%3+n%4+.........n%n=,n<=10^12次要. 一味的找规律之初.没有发现.后来,前辈执教后,人才平淡,所以,现在唯一明确的. 首先在地图上: 对于该题 ...
- jquery自己主动旋转的登录界面的背景代码登录页背景图
在其他网站上看到比较爽Web登录界面.背景图片可以自己主动旋转. 介绍给大家.有兴趣的可以改改下来作为自己的系统登录界面. 如图: watermark/2/text/aHR0cDovL2Jsb2cuY ...
- android 控制手机的体积的大小 切换音效模式
(1)项目介绍 于android API的AudioManager于,它提供了一种方法来调整电话音量. audioMa.adjustVolume(AudioManager.ADJUST_LOWER, ...