前端測試存在的問題 在講Sinon之前,我們得先講一下在學習了Mocha、chai以及enzyme之後,我們的前端測試還存在的一些問題。 比如前臺測試需要與後臺交互,獲取後臺數據後再根據相應數據進行測試。 又比如一個函數測試依賴另一個函數,我們可以根據測試的目的去模擬另一個函數,講兩者的測試分開,從 ...
前端測試存在的問題
在講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的另一種玩法,即對原有函數的監控玩法,在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);
});
測試結果如下:
根據測試結果可以瞭解到,原函數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();
運行上述測試用例得到以下結果:
小結
Sinon主要是一個測試輔助工具,通過偽裝和攔截,來模擬與其他系統或函數的操作,可以解耦測試的依賴。
在上面只講到了Sinon的spy、stub和mock三個函數,其實還有fake XHR(模擬xhr請求)、fack server(模擬伺服器)以及fake timer(模擬定時器)等操作。這裡就不多講了,具體的可以查看此API:Sinon v4.1.6。