一、總體概念
jest單元測試的寫法為三步,引入測試內容,運行測試內容,最后進行比較,是否達到預期。
Jest中的斷言使用expect, 它接受一個參數,就是運行測試內容的結果,返回一個對象,這個對象來調用匹配器(toBe/。。。。) ,
匹配器的參數就是我們的預期結果,這樣就可以對結果和預期進行對比了,也就可以判斷對不對了
1、兩個必會的方法
-
test方法:Jest封裝的測試方法,一般填寫兩個參數,描述和測試方法
-
expect方法 :預期方法,就是你調用了什么方法,傳遞了什么參數,得到的預期是什么
2、匹配器:
- toBe():絕對相等(===)
- toEqual():簡單類型絕對匹配;復雜類型內容結果的匹配
- toBeNull():匹配null
- toBeUndefined():匹配undefined
- toBeDefined():匹配非undefined
- toBeTruthy():匹配轉化后為true
- toBeFalsy():匹配轉化后為false
- toBeGreaterThan():相當於大於號
- toBeLessThan():相當於小於號
- toBeGreaterThanOrEqual():相當於大於等於號
- toBeLessThanOrEqual():相當於大於等於號
- toBeCloseTo():解決js浮點錯誤
- toMatch(regExp/string):用正則表達式或者字符串匹配字符串片段
- toContain():匹配數組或者Set中的某一項
- toThrow():匹配異常處理,如果拋出了異常就過測試用例
expect({a:1}).toBe({a:1})//判斷兩個對象是否相等 expect(1).not.toBe(2)//判斷不等 expect(n).toBeNull(); //判斷是否為null expect(n).toBeUndefined(); //判斷是否為undefined expect(n).toBeDefined(); //判斷結果與toBeUndefined相反 expect(n).toBeTruthy(); //判斷結果為true expect(n).toBeFalsy(); //判斷結果為false expect(value).toBeGreaterThan(3); //大於3 expect(value).toBeGreaterThanOrEqual(3.5); //大於等於3.5 expect(value).toBeLessThan(5); //小於5 expect(value).toBeLessThanOrEqual(4.5); //小於等於4.5 expect(value).toBeCloseTo(0.3); // 浮點數判斷相等 expect('Christoph').toMatch(/stop/); //正則表達式判斷 expect(['one','two']).toContain('one'); //匹配數組 function compileAndroidCode() { throw new ConfigError('you are using the wrong JDK'); } test('compiling android goes as expected', () => { expect(compileAndroidCode).toThrow(); expect(compileAndroidCode).toThrow(ConfigError); //判斷拋出異常 })
3、describe()測試分組
Jest
為我們提供了一個分組的語法describe()
,創建一個測試集。
這個方法接受兩個參數,它的語法和test 的一致,第一個參數也是字符串,對這一組測試進行描述, 第二個參數是一個函數,函數體就是一個個的test 測試。
在jest中,test和it一樣,接受兩個參數,第一個是字符串,對這個測試進行描述,需要什么條件,達到什么效果。第二個是函數,函數體就是真正的測試代碼,jest 要執行的代碼
對一個功能進行測試,但它分為多種情況,需要多個test或it, 最好使用descibe() 把多個test 包起來,形成一組測試。只有這一組都測試完成之后,才能說明這個功能是好的。
import {isTrueOrFasle} form './tools' ; describe('true or false', () => { it('should return true when input true', () => { let result = isTrueOrFasle(true); expect(result).toBeTruthy(); // toBeTruthy 匹配器 }) test('should return false when input fasle', () => { let result = isTrueOrFasle(false); expect(result).toBeFalsy(); // toBeFalsy 匹配器 }) })
4、異步代碼測試
回調函數/promise/asyn await
參考:https://www.cnblogs.com/SamWeb/p/11454923.html
expect.assertions(1); //斷言,表示必須執行一次expect 代碼才算執行完
test('test axios async await', async() => { const res = await fetchThreeData(); expect(res.data).toEqual({ success: true }) })
5、Mock函數
有時進行單元測試時,要測試的內容依賴其他內容,比如異步請求,會依賴網絡,很可能造成測試達不到效果。 能不能把依賴變成可控的內容?這就用到Mock。Mock就是把依賴替換成我們可控的內容,實現測試的內容和它的依賴項隔離。那怎么才能實現mock呢?使用Mock 函數。在jest中,當我們談論Mock的時候,其實談論的就是使用Mock 函數代替依賴。Mock函數就是一個虛擬的或假的函數,所以對它來說,最重要的就是實現依賴的全部功能,從而起到替換的作用。通常,mock函數會提供以下三個功能,來實現替換:函數的調用捕獲,設置函數返回值,改變原函數的實現。
在jest 創建一個Mock 函數最簡單的方法就是調用jest.fn() 方法。
1.函數的調用捕獲。捕獲調用指的是這個函數有沒有被調用,調用的參數是什么,返回值是什么,通常用於測試回調函數,模擬真實的回調函數。就像下邊的forEachFun函數,它接受一個回調函數,每個調用者都會傳遞不同的回調函數過來,我們事先並不知道回調函數,再者我們測試forEach 的重點是,該函數是不是把數組中的每一項都傳遞給回調函數了,所以回調函數只要是一個函數就可以了,但該函數必須把調用的信息都保存下來,這就是Mock 函數的調用捕獲,為此mock 函數還有一個mock 屬性。
測試中就使用jest.fn() 生成的mock 函數來模擬真實的回調函數。
// mock的第一個用處:調用函數的捕獲 export const forEachFun = (array: any[], callback: Function) => { array.forEach((i) => callback(i)); }; test('should call callback everyone', () => { const mockFun = jest.fn(); // 模擬函數 const testArr = [1, 2]; forEachFun(testArr, mockFun); console.log(30, mockFun.mock); expect(mockFun.mock.calls.length).toBe(2); });
mock函數mockFun的mock 屬性是一個對象,打印結果:
{
calls: [ [ 1 ], [ 2 ] ], instances: [ undefined, undefined ], invocationCallOrder: [ 1, 2 ], results:[
{ type: 'return', value: undefined }, { type: 'return', value: undefined }
]
}
- calls 保存的就是調用狀態。calls 是一個數組,每一次的調用都組成數組的一個元素,在這里調用了兩次,就有兩個元素。每一個元素又是一個數組,它則表示的是函數調用時的參數,因為每次的調用都傳遞了一個參數給函數,所以數組只有一項。如果有多個參數,數組就有多項,按照函數中的參數列表依次排列。這時候,就可以做斷言,函數調用了幾次,就判斷calls.length. expect(mockFun.mock.calls.length).toBe(2) 就是斷言函數是不是調用了兩次。expcet(mockFun.mock.calls[0][0]) .toBe(1)就是斷言第一次調用的時候傳遞的參數是不是1. 可能覺得麻煩了, 的確有點麻煩了,幸好,jest 對函數的mock參數進行了簡單的封裝,提供了簡單的匹配器:
toHaveBeenCalled()/toBeCalled():用來判斷mock函數是否被掉用過;
toHaveBeenCalledTimes(number)/toBeCalledTimes(number):用來判斷mock函數調用過幾次;
toHaveBeenCalledWith(arg1,arg2,...)/toBeCalledWith(arg1,arg2,...):用來判斷是否使用了特定參數調mock函數
..... 參考官網
test('should call callback everyone', () => { const mockFun = jest.fn(); const testArr = [1, 2]; forEachFun(testArr, mockFun); expect(mockFun).toHaveBeenCalled(); });
- results保存的就是返回值。
2.設置函數返回值。有的時候,不想調用函數,直接獲取到函數的返回值就可以了,比如異步函數, 以fetchData 為例,它直接返回一個promise 就好了,根本沒有必要請求服務器。
mock函數有mockReturnValue(), 它的參數就是返回值。不過它不能返回promise.。
可以使用mockResolvedValue直接返回promise的值. 對fetchData 進行mock, 然后設置它的mockResolvedValue()
test('should return data when fetchData request success', () => { const fetchData = jest.fn(); fetchData.mockResolvedValue({name: 'sam'}) return fetchData().then(res => { expect(res).toEqual({name: 'sam'}) }) })
3.改變函數實現。 有時不想使用默認的mock函數jest.fn(),尤其是測試回調函數的時候,你想提供回調函數實現,比如上面的forEach, 確實寫一個真實的回調函數進行測試,心里更有底一點。mock 函數實現也有兩種方法,jest.fn() 可以接受一個參數,這個參數就可以是一個函數實現。forEach 中的mock 函數就可以成mock 函數提供了一個方法mockImplementation(), 它的參數也是一個函數實現,使用mockImplementation() 來mock fetchData,讓它返回{name: 'sam'}
注:VSCode的終端窗口中輸入yarn test
就可以進行測試了
- 配置jest :npx jest --init
- 生成代碼覆蓋率:npx jest --coverage
- Jest識別三種測試文件:
- 測試文件后綴為js,jsx,ts,tsx
- 測試文件需要放在tests/unit/目錄下或者是/__tests__/目錄下
- 以xx.test.js/...結尾的文件,以xx.spec.js/...結尾的文件,
只要滿足這三個要求的測試文件,使用運行jest時就會自動執行