单元测试期间, 被测单元会存在一些外部依赖的状况。javascript
而单测中咱们更加注重被测对象自身的功能和行为,以及与外部依赖的交互行为,好比是否调用,调用中参数,调用的次数和顺序以及返回的结果或发生的异常等,并不须要关注外部依赖的具体细节。html
固然你页能够选择这两部分一块儿测试,可大声念一遍咱们的术语:「单元测试」。这样是否已经和咱们测试的原则相违背了,可缺失被测单元再缺失外部依赖的状况下,测试是不能正常进行的,咱们陷入了两难境界,是否要继续遵照咱们的原则?java
为了让咱们够能贯彻咱们原则,出现了stub 和 mock 此类对象,其功能就是代替外部依赖,从而知足模拟真实场景,帮助咱们的单元测试顺利进行。git
那如何分辨那些是 stub 和 mock 呢?接下来们来辨析一下这两个概念。github
stub 和 mock 对象的出现,其做用都是代替外部依赖,帮助咱们顺利进行单元测试。单元测试
Test lifecycle with stubs: Setup - Prepare object that is being tested and its stubs collaborators. Exercise - Test the functionality. Verify state - Use asserts to check object's state. Teardown - Clean up resources.测试
Test lifecycle with mocks: Setup data - Prepare object that is being tested. Setup expectations - Prepare expectations in mock that is being used by primary object. Exercise - Test the functionality. Verify expectations - Verify that correct methods has been invoked in mock. Verify state - Use asserts to check object's state. Teardown - Clean up resources.this
stub 和 mock 在测试的生命周期中表现不一样,stub 协助被测单元进行自身的功能的校验,mock 更可能是测试被测单元的行为是否符合预期,且自身在被测单元行为触发后进行验证。spa
看似说的比较玄乎,咱们来看看对应不一样类型下的代码段:.net
//产品代码
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);
});
});
复制代码
//产品代码
function A(b) {
this.num = 0;
this._b = b;
}
A.prototype.run = function () {
this.num = this._b.getNum(2);
};
//测试代码(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, 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 好像比 stub 更加高级,由于 mock 能够再模拟真实环境下,协助被测单元进行测试自身功能,而且对于被测单元的对外部的行为同时进行测试,因此感受是高级一些。
实际状况下,咱们编写的代码段更可能是符合 stub 流程中的测试。可表现形式上,mock流程代码不是更高级吗?为何现实使用中,咱们并无使用 mock 流程中的代码?
由于在一般状况下,咱们使用的 mock 是退化的mock,从而是的 mock 流程和 stub 流程在测试使用上行为一致。其实咱们是把 mock 当作了 stub 进行使用,其主要区别是 mock 建立时期就没有添加对应的后续指望,致使后期也用不着进行验证。就像这样:
//产品代码
function A(b) {
this.num = 0;
this._b = b;
}
A.prototype.run = function () {
this.num = this._b.getNum(2);
};
//测试代码(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(1); //只指定返回值,没有指望的参数或指望调用的次数。所以不用verify来验证了!
var a = new A(mockB);
a.run();
expect(a.num).toEqual(1);
});
});
复制代码
为何咱们出现这样状况?颇有多是由于在平常使用中,咱们对二者的概念边界都比较模糊,并且开发中常常涉及 mock 数据等术语,更容易干扰咱们在单元测试语境下对 mock 的定义。因此在Jest社区中由于区分命名也进行过对应的讨论。Rename jest.mock to jest.stub
PS: 以上案例代码摘自参考文献。