這篇文章帶給大家的內容是關於如何利用Jest測試JavaScript(Mock函數),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。
在本篇教學中,我們會介紹 Jest 中的三個與 Mock 函數相關的API,分別是jest.fn()、jest.spyOn()、jest.mock()。使用它們創建Mock函數能夠幫助我們更好的測試項目中一些邏輯較複雜的程式碼,例如測試函數的巢狀調用,回調函數的調用等。
如果你還不知道Jest的基本使用方法,請先閱讀: http://www.php.cn/js-tutorial-411835.html
為什麼要使用Mock函數?
在專案中,一個模組的方法內常常會去呼叫另外一個模組的方法。在單元測試中,我們可能不需要關心內部呼叫的方法的執行過程和結果,只想知道它是否被正確呼叫即可,甚至會指定該函數的回傳值。此時,使用Mock函數是十分有必要。
Mock函數提供的以下三種特性,在我們寫入測試程式碼時十分有用:
#擷取函數呼叫情況
設定函數傳回值
改變函數的內部實作
#我們接著使用上篇文章中的目錄結構,在test/functions .test.js檔案中寫測試程式碼,src/目錄下寫被測試程式碼。
1. jest.fn()
jest.fn()是創建Mock函數最簡單的方式,如果沒有定義函數內部的實現,jest.fn ()會傳回undefined作為回傳值。
// functions.test.js test('测试jest.fn()调用', () => { let mockFn = jest.fn(); let result = mockFn(1, 2, 3); // 断言mockFn的执行后返回undefined expect(result).toBeUndefined(); // 断言mockFn被调用 expect(mockFn).toBeCalled(); // 断言mockFn被调用了一次 expect(mockFn).toBeCalledTimes(1); // 断言mockFn传入的参数为1, 2, 3 expect(mockFn).toHaveBeenCalledWith(1, 2, 3); })
jest.fn()所建立的Mock函數也可以設定回傳值,定義內部實作或傳回Promise物件。
// functions.test.js test('测试jest.fn()返回固定值', () => { let mockFn = jest.fn().mockReturnValue('default'); // 断言mockFn执行后返回值为default expect(mockFn()).toBe('default'); }) test('测试jest.fn()内部实现', () => { let mockFn = jest.fn((num1, num2) => { return num1 * num2; }) // 断言mockFn执行后返回100 expect(mockFn(10, 10)).toBe(100); }) test('测试jest.fn()返回Promise', async () => { let mockFn = jest.fn().mockResolvedValue('default'); let result = await mockFn(); // 断言mockFn通过await关键字执行后返回值为default expect(result).toBe('default'); // 断言mockFn调用后返回的是Promise对象 expect(Object.prototype.toString.call(mockFn())).toBe("[object Promise]"); })
上面的程式碼是jest.fn()提供的幾個常用的API和斷言語句,下面我們在src/fetch.js檔案中寫一些被測試程式碼,以更接近業務的方式來理解Mock函數的實際應用。
被測試程式碼中依賴了axios這個常用的請求庫和JSONPlaceholder這個上篇文章中提到免費的請求接口,請先在shell中執行npm install axios --save安裝依賴。
// fetch.js import axios from 'axios'; export default { async fetchPostsList(callback) { return axios.get('https://jsonplaceholder.typicode.com/posts').then(res => { return callback(res.data); }) } }
我們在fetch.js中封裝了一個fetchPostsList方法,該方法請求了JSONPlaceholder提供的接口,並透過傳入的回呼函數返回處理過的返回值。如果我們想要測試該介面能夠被正常請求,只需要捕獲傳入的回呼函數能夠被正常的呼叫即可。下面是functions.test.js中的測試的程式碼。
import fetch from '../src/fetch.js' test('fetchPostsList中的回调函数应该能够被调用', async () => { expect.assertions(1); let mockFn = jest.fn(); await fetch.fetchPostsList(mockFn); // 断言mockFn被调用 expect(mockFn).toBeCalled(); })
2. jest.mock()
fetch.js資料夾中封裝的請求方法可能我們在其他模組被呼叫的時候,並不需要進行實際的請求(請求方法已經透過單側或需要該方法傳回非真實資料)。此時,使用jest.mock()去mock整個模組是十分必要的。
下面我們在src/fetch.js的同級目錄下建立一個src/events.js。
// events.js import fetch from './fetch'; export default { async getPostList() { return fetch.fetchPostsList(data => { console.log('fetchPostsList be called!'); // do something }); } }
functions.test.js中的測試程式碼如下:
// functions.test.js import events from '../src/events'; import fetch from '../src/fetch'; jest.mock('../src/fetch.js'); test('mock 整个 fetch.js模块', async () => { expect.assertions(2); await events.getPostList(); expect(fetch.fetchPostsList).toHaveBeenCalled(); expect(fetch.fetchPostsList).toHaveBeenCalledTimes(1); });
在測試程式碼中我們使用了jest.mock('../src/fetch.js')去mock整個fetch.js模組。如果註解掉這行程式碼,執行測試腳本時會出現以下報錯訊息
#從這個報錯訊息中,我們可以總結出一個重要的結論:
在jest中如果想捕獲函數的呼叫情況,則函數必須被mock或spy!
3. jest.spyOn()
jest.spyOn()方法同樣建立一個mock函數,但是該mock函數不僅能夠捕獲函數的呼叫情況,還可以正常的執行被spy的函數。實際上,jest.spyOn()是jest.fn()的語法糖,它創建了一個和被spy的函數具有相同內部程式碼的mock函數。
上圖是先前jest.mock()的範例程式碼中的正確執行結果的截圖,從shell腳本中可以看到console. log('fetchPostsList be called!');這行程式碼並沒有在shell中被列印,這是因為經過jest.mock()後,模組內的方法是不會被jest所實際執行的。這時我們就需要使用jest.spyOn()。
// functions.test.js import events from '../src/events'; import fetch from '../src/fetch'; test('使用jest.spyOn()监控fetch.fetchPostsList被正常调用', async() => { expect.assertions(2); const spyFn = jest.spyOn(fetch, 'fetchPostsList'); await events.getPostList(); expect(spyFn).toHaveBeenCalled(); expect(spyFn).toHaveBeenCalledTimes(1); })
執行npm run test後,可以看到shell中的列印訊息,說明透過jest.spyOn(),fetchPostsList被正常的執行了。
4. 總結
這篇文章中我們介紹了jest.fn(),jest .mock()和jest.spyOn()來創建mock函數,透過mock函數我們可以透過以下三個特性去更好的編寫我們的測試程式碼:
捕獲函數呼叫情況
設定函數傳回值
改變函數的內部實作
在實際專案的單元測試中,jest.fn()常被用來進行某些有回呼函數的測試;jest.mock()可以mock整個模組中的方法,當某個模組已經被單元測試100%覆蓋時,使用jest.mock()去mock該模組,節約測試時間和測試的冗餘度是十分必要;當需要測試某些必須被完整執行的方法時,常常需要使用jest.spyOn()。這些都需要開發者根據實際的業務代碼靈活選擇。
以上是如何利用Jest測試JavaScript(Mock函數)的詳細內容。更多資訊請關注PHP中文網其他相關文章!