單元測試期間, 被測單元會存在一些外部依賴的狀況。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: 以上案例代碼摘自參考文獻。