我对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 = ;
}
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 ;
}
} var a = new A();
a.run(); expect(a.num).toEqual();
});
});
示例2(替换类):
//产品代码
function A() {
this.num = ;
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 ;
}; var a = new A();
a.run(); expect(a.num).toEqual();
});
});
Test Stub(测试桩)
测试桩与假对象有点类似,也要实现与产品代码耦合的组件,指定期望的行为。这里最大的不同是测试桩需要注入到产品代码中,从而在测试产品代码时替换组件,执行桩的行为。使用测试桩不需要进行备份和还原。
示例:
//产品代码
function A(b) {
this.num = ;
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 ;
}
}; var a = new A(stub_B); //注入桩
a.run(); expect(a.num).toEqual();
});
});
Test Spy(嗅探桩)
与测试桩类似,但是可以记录桩使用的记录,并进行验证。
示例:
可以使用jasmine的spy来举例。
//产品代码
function A(b) {
this.num = ;
this._b = b;
}
A.prototype.run = function () {
this.num = this._b.getNum();
}; //测试代码
describe("测试A类的run方法", function () {
it("获得数字", function () {
var stub_b = {
getNum: function(){
return ;
}
};
spyOn(stub_b, "getNum").andCallThrough(); //嗅探桩的getNum方法 var a = new A(stub_b); //注入桩
a.run(); expect(a.num).toEqual();
expect(stub_b.getNum).toHaveBeenCalled(); //验证调用过桩的getNum方法
});
});
Mock Object(模拟对象)
设定产品代码中耦合的类的期望的行为,然后验证期望的行为是否发生,从而达到测试产品代码行为的目的。适用于验证一些void的行为。例如:在某个条件发生时,要记录Log。这种情景,用stub就很难验证,因为对目标物件来说,沒有回传值,也沒有状态变化,就只能通过mock object來验证目标物件是否正确的与Log介面进行互动。
示例:
//产品代码
function A(b) {
this.num = ;
this._b = b;
}
A.prototype.run = function () {
this.num = this._b.getNum();
}; //测试代码(Mock为伪代码)
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().times(); var a = new A(mockB);
a.run(); expect(a.num).toEqual();
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 = ;
this._b = b;
}
A.prototype.run = function () {
this.num = this._b.getNum();
}; //测试代码(Mock为伪代码)
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(); //只指定返回值,没有期望的参数或期望调用的次数。因此不用verify来验证了! var a = new A(mockB);
a.run(); expect(a.num).toEqual();
});
});
也可以用Stub来达到相同的效果:
//产品代码
function A(b) {
this.num = ;
this._b = b;
}
A.prototype.run = function () {
this.num = this._b.getNum();
}; //测试代码
describe("测试A类的run方法", function () {
it("获得数字", function () {
var stub_B = {
getNum: function(){
return ;
}
}; var a = new A(stub_B);
a.run(); expect(a.num).toEqual();
});
});
总结
在比较简单的情况下(如需要哑对象来通过编译,或是需要测试桩来替换耦合的组件),使用Stub。
如果需要验证耦合组件的行为,可以使用Spy或Mock。
参考资料
《xUnit测试模式--测试码重构》
我对Stub和Mock的理解的更多相关文章
- 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 ...
随机推荐
- pygame加载中文名mp3文件出现error
好一阵子没有写东西了,最近几天在做一个基于Python pygame的音乐播放器,本来想做完了,再来发篇文章的,可越做越深,框架大致出来了,考虑周期比较长,也可能是我个人问题,做得比较慢,最近.下面来 ...
- python+request+robot framework接口自动化测试
python+requests实现接口的请求前篇已经介绍,还有不懂或者疑问的可以访问 python+request接口自动化框架 目前我们需要考虑的是如何实现关键字驱动实现接口自动化输出,通过关键字的 ...
- Servlet3.0新特性使用详解
可插拔的Web框架 几乎所有基于Java的web框架都建立在servlet之上.现今大多数web框架要么通过servlet.要么通过Web.xml插入.利用标注(Annotation)来定义servl ...
- JDK中日期和时间的几个常用类浅析(四)
java.time.Instant java.time.Instant类对应的是时间线上的一个时间点.该类通过保存着从格林威治的起始时间(1970年一月一日零点零分)开始计算所经过的纳妙数来表示时 ...
- GNU/Linux与开源文化的那些人和事
一.计算机的发明 世上本无路,走的人多了,就有了路.世上本无计算机,琢磨的人多了--没有计算机,一切无从谈起. 三个人对计算机的发明功不可没,居功至伟.阿兰·图灵(Alan Mathison Tur ...
- 如何在IDEA中调试 Jar文件
原创文章,转载请注明出处:http://www.cnblogs.com/acm-bingzi/p/6668333.html 问题: 一般情况下,可以打成Jar包的项目,它的源码运行Applicat ...
- 使用cocapods报错 [!] Your Podfile has had smart quotes sanitised. To avoid issues in the future, you should not use TextEdit for editing it. If you are not using TextEdit, you should turn off smart quotes
从github上下载的工程大部分都使用了cocapods,在install的时候可能会报错. 报错原因: 1.不要使用文本编辑去编辑Podfile文件,使用Xcode编辑,或者使用终端敲命令去编辑. ...
- Android查看stdout 和stderr
在默认状态下,Android系统有stdout和stderr(System.out和System.err)输出到/dev/null,在运 行Dalvik VM的进程中,有一个系统可以备份日志文 ...
- java小题:福尔摩斯的约会
原题地址:https://www.nowcoder.com/pat/6/problem/4040 防止广告嫌疑,原题为: 题目描述 大侦探福尔摩斯接到一张奇怪的字条:"我们约会吧! 3485 ...
- MFC使用SQLite 学习系列 一: SQLITE_MISUSE错误
一 为什么要选择SQLite 由于使用文本文件来记录测试数据,速度越来越慢的问题,经过园友推荐,使用了SQLite来进行数据的存储,再次感谢园友@LightSmaile. 关于这个问题,可以参考一下上 ...