【前端單元測試入門03】Sinon


前端測試存在的問題

在講Sinon之前,我們得先講一下在學習了Mocha、chai以及enzyme之后,我們的前端測試還存在的一些問題。
比如前台測試需要與后台交互,獲取后台數據后再根據相應數據進行測試。
又比如一個函數測試依賴另一個函數,我們可以根據測試的目的去模擬另一個函數,講兩者的測試分開,從而達到測試中也能解耦的目的。

測試輔助工具Sinon

Sinon是用來輔助我們進行前端測試的,在我們的代碼需要與其他系統或者函數對接時,它可以模擬這些場景,從而使我們測試的時候不再依賴這些場景。
Sinon有主要有三個方法輔助我們進行測試:spy,stub,mock。

Sinon的安裝

在講解用法前,先看一下我們的測試項目結構:
項目結構

然后這里的測試例子用的是官網上的例子,once.js的內容是:

export default function once(fn) {
    var returnValue, called = false;
    return function () {
        if (!called) {
            called = true;
            returnValue = fn.apply(this, arguments);
        }
        return returnValue;
    };
}

once.test.js的內容為空。
那么接着安裝Sinon

npm install --save-dev sinon

Sinon之spy

官方對spy的解釋:

A test spy is a function that records arguments, return value, the value of this and exception thrown (if any) for all its calls. There are two types of spies: Some are anonymous functions, while others wrap methods that already exist in the system under test.

spy生成一個間諜函數,它會記錄下函數調用的參數,返回值,this的值,以及拋出的異常。
而spy一般有兩種玩法,一種是生成一個新的匿名間諜函數,另外一種是對原有的函數進行封裝並進行監聽。

搭好上面的結構后,直接在once.test.js里面寫入spy的使用例子:

import {assert} from 'chai'
import sinon from 'sinon'
import once from '../src/once'

describe('測試Once函數', function () {
  it('傳入Once的函數會被調用', function () {
    var callback = sinon.spy();
    var proxy = once(callback);

    proxy();

    assert(callback.called);
  });
})

如上面代碼所示,sinon.spy()會產生一個函數對象,當once調用這個函數對象后,這個函數對象通過called可以返回一個bool值,表示函數是否被調用。
測試結果為:
spy匿名函數測試結果

現在來看看spy的另一種玩法,即對原有函數的監控玩法,在once.test.js中加入以下測試用例:

it('對原有函數的spy封裝,可以監聽原有函數的調用情況', function () {
    const obj={
        func:()=>{
            return 1+1
        }
    }
    sinon.spy(obj,'func')

    obj.func(3);

    assert(obj.func.calledOnce)
    assert.equal(obj.func.getCall(0).args[0], 3);
});

測試結果:
對原有函數的監聽測試結果

更多spy的API請參考
SInon的spy

Sinon之Stub

來看看Stub的官方介紹:

Test stubs are functions (spies) with pre-programmed behavior.
They support the full test spy API in addition to methods which can be used to alter the stub’s behavior.
As spies, stubs can be either anonymous, or wrap existing functions. When wrapping an existing function with a stub, the original function is not called.

stub是帶有預編程行為的函數。
簡單點說,就是spy的加強版,不僅完全支持spy的各種操作,還能操作函數的行為。
和spy一樣,stub也能匿名,也能去封住並監聽已有函數。
然而有一點和spy不同,當封裝了一個已有函數后,原函數不會再被調用。

對於匿名的玩法我們就不說了,直接來封裝的玩法,以下是對之前spy封裝的修改:

it('對原有函數的stub封裝,可以監聽原有函數的調用情況,以及模擬返回', function () {
    const obj={
        func:()=>{
           console.info(1)
        }
    }
    sinon.stub(obj,'func').returns(42)

    const result=obj.func(3);

    assert(obj.func.calledOnce)
    assert.equal(obj.func.getCall(0).args[0], 3);
    assert.equal(result,43);
});

測試結果如下:
stub用法的測試結果

根據測試結果可以了解到,原函數func的內容確實沒有被執行,因為沒有打印1。
更多API查看Sinon之stub

Sinon之mock

看一下官網的介紹

Mocks (and mock expectations) are fake methods (like spies) with pre-programmed behavior (like stubs) as well as pre-programmed expectations.
A mock will fail your test if it is not used as expected.

大致意思就是mock像spy和stub一樣的偽裝方法,如果mock沒有得到期望的結果就會測試失敗。

這里的話可能講述不是很清楚,那么看一下代碼就很好理解了:

it('mock的測試', function () {
    var myAPI = { 
        method: function () {
            console.info("運行method")
        },
        func: function () {
            console.info("運行method")
        }
    };

    var mock = sinon.mock(myAPI);
    mock.expects("method").once().returns(2);
    mock.expects("func").twice()

    myAPI.method();
    myAPI.func();
    myAPI.func();

    mock.verify();
});

在以上代碼中,mock其實和stub很像,只不過是stub是對對象中單個函數的監聽和攔截,而mock是對多個。
mock首先會對函數進行一個預期:

   var mock = sinon.mock(myAPI);
   mock.expects("method").once().returns(2);
   mock.expects("func").twice()

比如once就是預期運行一次,如果最終驗證時函數沒有被執行或者執行多次都會拋出錯誤。
也可以操作返回結果,比如像stub一樣returns(2)依然有效。
而且與stub一樣,在mock監聽后,原有函數內容將不會執行。

在進行了預期操作后,就對函數進行實際操作:

myAPI.method();
myAPI.func();
myAPI.func();

最后再進行驗證操作:

mock.verify();

運行上述測試用例得到以下結果:
mock的測試用例結果

小結

Sinon主要是一個測試輔助工具,通過偽裝和攔截,來模擬與其他系統或函數的操作,可以解耦測試的依賴。
在上面只講到了Sinon的spy、stub和mock三個函數,其實還有fake XHR(模擬xhr請求)、fack server(模擬服務器)以及fake timer(模擬定時器)等操作。這里就不多講了,具體的可以查看此API:Sinon v4.1.6


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM