iT邦幫忙

2023 iThome 鐵人賽

DAY 27
0
自我挑戰組

Unit Test 學習路系列 第 27

Day 26: How to test customized useHooks?

  • 分享至 

  • xImage
  •  

今天來練習一下,如何對自己設計的 useHooks 進行測試。

(一) 建立 useHooks
(二) 認識 renderHook()
(三) 認識 act()


建立一個測試練習的 useHooks

建立一計算±1的計算機,初始值為0,可由參數傳入,回傳一物件,包含目前數值、遞增函式、遞減函式。

import { useState } from "react";

interface CounterProps {
    intialState?: number 
}
export default function useCounter({intialState = 0}: CounterProps){
    const [ count, setCount ] = useState(intialState);

    const increment = () => setCount(pre => pre + 1);
    const decrement = () => setCount(pre => pre - 1);

    return({
        count,
        increment,
        decrement
    })
}

撰寫測試:count 設定初始值為 0 是否有正確顯示。

import { renderHook } from "../../test-utils";
import useCounter from "./useCounter";

describe("useCounter", () => {
    test("Render initialState: 0 when no props passing", () => {
        const { result }  = renderHook(() => useCounter({}));
        expect(result.current.count).toBe(0);
    })
})

測試結果:
PASS src/hooks/useCounter/useCounter.test.tsx

補充說明:renderHook

我們這幾週以來撰寫測試會使用 render() 去模擬渲染元件。但在這裡的 useCounter 並非 return JSX,因此不適用。而且,以往我們在引入自定義 Hooks 時,有個先決條件:不能在 Hooks 以外的地方進行使用。這時候,RTL 提供的 renderHook 方法就派上用場了!

renderHook 是一函式,可以接收兩參數:callBack functions<options>

  • callBack functions 接收 useHooks 為測試項目。
  • 第二個參數為選填項目,可以接收物件內容,包含 intialProps 或 前幾天提到的 wrapper
    useCounter 為例,我們可以這樣寫:
// 寫法一:
renderHook(() => useCounter({}));

// 寫法二:
renderHook(useCounter, {
    initialProps: {}
});

renderHook 回傳的是一物件,內容包含:

{
    result: {
      all: Array<any>
      current: any, // useHooks 回傳的內容
      error: Error
    }
}

以上述的程式碼,我們使用物件解構的方式取得 result:

const { result }  = renderHook(() => useCounter({}));

撰寫測試:執行遞增函式、遞減函式,數值是否會 ±1

import { act, renderHook } from "../../test-utils";
import useCounter from "./useCounter";

describe("useCounter", () => {
    test("Render plus 1 by using increment function", () => {
        const { result }  = renderHook(() => useCounter({}));
        act(() => result.current.increment());
        expect(result.current.count).toBe(1);
        console.log("🚀 result.current.count:", result.current.count)
    })

    test("Render minus 1 by using decrement function", () => {
        const { result }  = renderHook(() => useCounter({}));
        act(() => result.current.decrement());
        expect(result.current.count).toBe(-1);
        console.log("🚀 result.current.count:", result.current.count)
    })
})

測試結果:
PASS src/hooks/useCounter/useCounter.test.tsx
console.log
🚀 result.current.count: 1
console.log
🚀 result.current.count: -1

補充說明:act()

  • To prepare a component for assertions, wrap the code rendering it and performing updates inside an act() call. This makes your test run closer to how React works in the browser.
  • 當我要在測試中更新 state,並確保更新state後才進行測試,可以搭配 RTL 提供的 act() 方法,確定 React state 已更新完成。

參考資源


上一篇
Day 25: Customized Render Functions
下一篇
Day 27: Jest Mocking - jest.fn()
系列文
Unit Test 學習路31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言