iT邦幫忙

2023 iThome 鐵人賽

DAY 28
0
自我挑戰組

Unit Test 學習路系列 第 28

Day 27: Jest Mocking - jest.fn()

  • 分享至 

  • xImage
  •  

我們今天把學習戰場拉回 Jest Mocking,了解一下我們可以怎麼使用它完成更複雜的測試。

了解一下 Jest Mocking 是什麼
實作 jest.fn()


Jest Mocking 是什麼

  • 不是執行實際的程式邏輯 (包含實際執行函式邏輯 或 Call API),而是用來模擬函式執行,Call API的行為。
  • 好處是減少撰寫測試的複雜度,專注在回傳內容是否符合預期的行為。
  • 通常會定義函式的返回值或行為來確認結果。
  • 增加測試效率。想像有上百筆測試需要 Call API 等待回傳...

實作 jest.fn()

假設情境:
我有一個元件 Counter,傳入三個 props,分別為:counthandleIncrementhandleDecrement。其中,兩個函式為選填。

interface CounterProps {
    count: number
    handeIncrement?: () => void
    handleDecrement?: () => void
}
export default function Counter(props: CounterProps){
    const {count, handeIncrement, handleDecrement} = props;

    return(
        <>
            <h1>{count}</h1>
            <div>
                {   handeIncrement 
                    && <button onClick={() => handeIncrement()}>Increment</button>
                }
                {   handleDecrement 
                    && <button onClick={() => handleDecrement()}>Decrement</button>
                }
            </div>
        </>
    )
}

撰寫測試一:確認畫面上有 <h1>{count}</h1>

import { render, screen } from "@testing-library/react";
import Counter from "./counter";
import userEvent from "@testing-library/user-event";

describe("Counter", () => {
    // (1) 確認畫面是否有正確顯示 UI
    test("Render count correctly!", () => {
        render(<Counter count={0}/>);

        const countEl = screen.getByRole("heading", {
            level: 1
        })
        expect(countEl).toBeInTheDocument();
        expect(countEl).toHaveTextContent("0");
    })
})

測試一結果:
PASS src/components/counter/counter.test.tsx


撰寫測試二:如果 props 有傳入函式,確認:(1)按鈕有出現在畫面上 (2)點擊按鈕,觸發呼叫函式。

補充說明:
使用 jest.fn() 創建一模擬函式
搭配 userEvent 觸發按鈕點擊(記得加入 async-await
toBeCalledTimes() 用在確認函式呼叫次數是否符合預期

describe("Counter", () => {
    // (2) 確認元件的 handeIncrement、handleDecrement 是否可以正確呼叫
    test("Call functions correctly", async() => {
        const handeIncrement = jest.fn();
        const handeDecrement = jest.fn();

        render(
            <CounterTwo
                count={10}
                handeIncrement={handeIncrement}
                handleDecrement={handeDecrement}
            />
        )

        const incrementBtn = screen.getByRole("button", {
            name: "Increment"
        })
        const decrementBtn = screen.getByRole("button", {
            name: "Decrement"
        })
        expect(incrementBtn).toBeInTheDocument();
        expect(decrementBtn).toBeInTheDocument();

        await userEvent.click(incrementBtn);
        await userEvent.click(decrementBtn);

        expect(handeIncrement).toBeCalledTimes(1);
        expect(handeDecrement).toBeCalledTimes(1);
    })
})

測試二結果:
PASS src/components/counter/counter.test.tsx

如果將 呼叫函數次數改為 2,像是:

    expect(handeDecrement).toBeCalledTimes(2);

測試結果:
FAIL src/components/counter/counter.test.tsx
● Counter › Call functions correctly
expect(jest.fn()).toBeCalledTimes(expected)

Expected number of calls: 2
Received number of calls: 1

參考資源


上一篇
Day 26: How to test customized useHooks?
下一篇
Day 28: Mocking HTTP Request (一)
系列文
Unit Test 學習路31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言