# 使用 Mock 撰寫測試(jest.fn, spyOn)
當在撰寫測試,希望專注在撰寫的測試本身(SUT),而不關注該測試的其他依賴函式或套件(DOC)時,就可以透過 Mock 來幫忙實現!
- [jest.fn():](https://hackmd.io/HPKnMQMtTs292NULu76JDQ#jestfn)
- 模擬一個函式
- 模擬函式回傳的其他方法
- 模擬依賴函式或套件
- [spyOn():](https://hackmd.io/HPKnMQMtTs292NULu76JDQ#spyOn)
- 模擬一個函式
- 需帶入兩個參數
```jsx!
jest.spyOn(object, methodName) // 物件名稱, 物件內的方法名稱
👉 jest.fn() 與 spyOn() 的差異:
jest.fn() 並不會實際去執行被 mock 的函式,而 spyOn 會去執行傳入的函式。**
### jest.fn()
首先來看一下範例程式碼,我們有兩個函式,其中 `checkRareMagicAttributes` 函式幫我們確認是否為稀有魔法屬性,而 `calculateRareAttributesTotal` 函式幫忙計算總人數,當在執行 `calculateRareAttributesTotal` 函式時是會透過傳入的 `checkRareMagicAttributes` 函式來幫我們確認稀有屬性的人數:
```jsx!
const quizList = [
name: "艾草",
attributes: "木"
name: "Vivian",
attributes: "火"
name: "啾啾",
attributes: "光"
// 確認是否為稀有魔法屬性
function checkRareMagicAttributes(attributes) {
if (attributes === "光" || attributes === "暗") {
return true;
} else {
return false;
// 確認屬性稀有屬性人數
function calculateRareAttributesTotal(data) {
let total = 0;
data.forEach((item) => {
if (checkRareMagicAttributes(item.attributes)) {
total += 1;
return total;
當針對 `calculateRareAttributesTotal` 撰寫測試而不希望實際去執行 `checkRareMagicAttributes` 函式時,可以透過 **`mock`** 來幫忙實現:
1. 首先透過 jest.fn() 來模擬一個函式:
```jsx
test('Introducing mocks', () => {
// arrange
// 模擬
const mockFunction = jest.fn();
checkRareMagicAttributes = mockFunction;
2. 接著透過 Jest 提供的方法來設定回傳值:
- `mockFn.mockReturnValue(value)`:mock 每次的回傳值
- `mockFn.mockReturnValueOnce(value)` :mock 一次回傳值,當沒有設定 `mockReturnValueOnce` 後會回傳 `mockReturnValue` 設定的回傳值
```jsx
test("Introducing mocks", () => {
// arrange
const mockFunction = jest.fn();
checkRareMagicAttributes = mockFunction;
// 設定回傳值
mockFunction.mockReturnValue(false).mockReturnValueOnce(true);
3. 斷言結果:可以透過 calculateRareAttributesTotal 回傳值及 toHaveBeenCalled 來判斷是否有成功呼叫 mockFunction 來斷言。
```jsx
test("Introducing mocks", () => {
// arrange
const mockFunction = jest.fn();
checkRareMagicAttributes = mockFunction;
mockFunction.mockReturnValue(false).mockReturnValueOnce(true);
// act
const result = calculateRareAttributesTotal(quizList);
// assert
expect(calculateRareAttributesTotal(quizList)).toBe(1);
expect(mockFunction).toHaveBeenCalled();
像這樣就透過 jest.fn() 的方式成功模擬一個函式!
### 模擬函式回傳的其他方法
**jest.fn()** 除了可以透過 `mockReturnValue` 來模擬回傳值外,還有 `mockImplementation` 方法,可以直接傳入一個模擬函式。
```jsx
// 範例程式碼取自官方文件
const mockFn = jest.fn(scalar => 42 + scalar);
mockFn(0); // 42
mockFn(1); // 43
mockFn.mockImplementation(scalar => 36 + scalar);
mockFn(2); // 38
mockFn(3); // 39
透過 Jest.fn() 可以更方便模擬依賴函式或套件!
const object = {
checkRareMagicAttributes: (attributes) => {
if (attributes === "光" || attributes === "暗") {
return true;
} else {
return false;
// 確認屬性稀有屬性人數
function calculateRareAttributesTotal(data) {
let total = 0;
data.forEach((item) => {
// 透過 checkRareMagicAttributes 的回傳值為 true / false 判斷是否為光暗屬性
if (checkRareMagicAttributes(item.attributes)) {
total += 1;
return total;
👉 當針對 `calculateRareAttributesTotal` 撰寫測試且希望有去執行 `checkRareMagicAttributes` 函式時,可以透過 `spyOn` 來幫忙實現:
1. 首先透過 `spyOn`,並帶入物件名稱及方法名稱 `checkRareMagicAttributes`:
```jsx
test("Introducing spyOn", () => {
// arrange
const spyOnFunction = jest.spyOn(object,"checkRareMagicAttributes");
2. 接著透過 result 來執行行動
```jsx
test("Introducing spyOn", () => {
// arrange
const spyOnFunction = jest.spyOn(object,"checkRareMagicAttributes");
// act
const result = calculateRareAttributesTotal(quizList);
3. 斷言結果:可以透過 `calculateRareAttributesTotal` 回傳值及 `toHaveBeenCalledTimes` 來判斷是否有成功呼叫 `spyOnFunction` 來斷言,這邊因為資料有三筆物件,所以共呼叫了三次 `spyOnFunction`。
```jsx!
test("Introducing spyOn", () => {
// arrange
const spyOnFunction = jest.spyOn(object,"checkRareMagicAttributes");
// act
const result = calculateRareAttributesTotal(quizList);
// assert
expect(result).toBe(1);
expect(spyOnFunction).toHaveBeenCalledTimes(3);
像這樣就透過 spyOn 模擬成功!
#### 可用 mockRestore() 恢復原始函式
`spyOn` 也可以使用 `mock` 的方法如 `mockImplementation` 等模擬回傳函式,而當想調用原始函式而不掉用模擬函式時 `spyOn` 還可以使用 `mockRestore()`` 的方式將模擬的函式改回原本函式,可用於多個測試其中一項須改回原本函式時。
https://jestjs.io/zh-Hans/docs/mock-functions
https://jestjs.io/docs/mock-function-api
https://medium.com/enjoy-life-enjoy-coding/jest-jojo是你-我的替身能力是-mock-4de73596ea6e
https://blog.jimmydc.com/mock-asynchronous-functions-with-jest/
https://vhudyma-blog.eu/3-ways-to-mock-axios-in-jest/
https://www.youtube.com/watch?v=1Xafx6o82Aw&ab_channel=SoftwareTestingHelp
https://dwatow.github.io/2020/04-24-jest/jest-doc-5/
https://jestjs.io/docs/jest-object#jestspyonobject-methodname
https://medium.com/enjoy-life-enjoy-coding/jest-jojo是你-我的替身能力是-mock-4de73596ea6e
https://www.youtube.com/watch?v=1Xafx6o82Aw&ab_channel=SoftwareTestingHelp
https://dwatow.github.io/2020/04-24-jest/jest-doc-5/