關於單元測試,咱們須要知道什麼?- 術語辨析篇

測試部分術語解釋

單元測試期間, 被測單元會存在一些外部依賴的狀況。javascript

而單測中咱們更加註重被測對象自身的功能和行爲,以及與外部依賴的交互行爲,好比是否調用,調用中參數,調用的次數和順序以及返回的結果或發生的異常等,並不須要關注外部依賴的具體細節。html

固然你頁能夠選擇這兩部分一塊兒測試,可大聲念一遍咱們的術語:「單元測試」。這樣是否已經和咱們測試的原則相違背了,可缺失被測單元再缺失外部依賴的狀況下,測試是不能正常進行的,咱們陷入了兩難境界,是否要繼續遵照咱們的原則?java

爲了讓咱們夠能貫徹咱們原則,出現了stub 和 mock 此類對象,其功能就是代替外部依賴,從而知足模擬真實場景,幫助咱們的單元測試順利進行。git

那如何分辨那些是 stub 和 mock 呢?接下來們來辨析一下這兩個概念。github

Stub v.s Mock

相同目的

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: 以上案例代碼摘自參考文獻。

參考文獻

  1. 我對Stub和Mock的理解
  2. 淺談mock和stub
  3. 極客時間-軟件測試52講-什麼是單元測試?如何作好單元測試?
相關文章
相關標籤/搜索