iT邦幫忙

2022 iThome 鐵人賽

DAY 28
0

雖然寫了那麼多,不論是有用的,或沒用的,要怎麼確保有沒有寫好呢?

當然就是就在講測試,雖然開發過程中可以測試功能正不正常,但隨著日後長大的需求有時難以回頭一一確認,測試就是為此而來!

但工作一陣子一直沒機會接觸(真的慚愧),就藉鐵人賽這個機會來認識看看!

前言

如果是用CRA的朋友,應該會看到這些已經安裝好了:

@testing-library/jest-dom,
@testing-library/react,
@testing-library/user-event,
  • @testing-library 預設使用 jest 來進行測試,方法都包含在 /jest-dom 其中,而在 setupTests.js 也先進行了 import,後續的測試也能直接取用。

  • /react 中包含許多可以與component and hook 與之互動的方法。
  • /user-event 是新的API,原本是 fireEvent,userEvent 是建立在fireEvent之上,更好的重現真正使用者互動的情境。

開始!

這次就拿前幾篇的 useToggle 來開刀!
詳細這篇:([DAY 09] 自己的Hook自己做!useToggle 再進化,useToggle+!)

測試方式

  • 親手實測 (略過)
  • 利用元件來測試 hook
  • 直接測試 hook

利用元件來測試 hook

那當然就需要先來寫一個元件:

import useToggle from "./hooks/useToggle"

function App({ on }) {
  const { isOn, toggle, toggleOn, toggleOff } = useToggle({ defaultState: on })
  return (
    <div>
      <h1>{isOn ? "on" : "off"}</h1>
      <button onClick={toggle}>toggle</button>
      <button onClick={toggleOn}>toggleOn</button>
      <button onClick={toggleOff}>toggleOff</button>
    </div>
  )
}

由於是DEMO就直接在CRA建立好之後使用原本的修改,可以按照實際情況管理相關的檔案

建立測試檔案:

useToggle/__test__/index.js

跑測試時,會去找 __test__ 的資料夾內或是名稱包含 .spec / .test,可以透過 jest.config.js 調整 testMatch

再來就是測試本身:

import { render, screen, renderHook, act } from "@testing-library/react"
import userEvent from "@testing-library/user-event"

import App from "App"

describe("Test with component", () => {
  test("should render component with default state false", () => {
    render(<App />)
    expect(screen.getByText("off")).toBeInTheDocument()
  })

  test("should display on with toggle button click", () => {
    render(<App />)
    expect(screen.getByText("off")).toBeInTheDocument()
    const button = screen.getByRole("button", { name: /^toggle$/i })
    userEvent.click(button)
    expect(screen.getByText("on")).toBeInTheDocument()
  })

  test("should display true from custom default state and display off after toggleOff button click", () => {
    render(<App on={true} />)
    expect(screen.getByText("on")).toBeInTheDocument()
    const button = screen.getByRole("button", { name: /^toggleOff$/i })
    userEvent.click(button)
    expect(screen.getByText("off")).toBeInTheDocument()
  })
})

這邊定義好了三種使用情境:

  • 初次顯示時應該要顯示off
  • 當按下按鈕時要顯示on
  • 初次顯示改顯示on,按下按鈕時顯示為off

而我們透過:

  • render 來渲染出真實的DOM來互動
  • screen.getBy... 來抓出需要的元素,透過 expect 來期望有無正確顯示,沒有就會報錯
  • userEvent 來模擬 click event

然後就可以跑起來:

npm run test

結果:

直接測試 hook

若不搭配元件,想直接測試 hook 本身也可以,可以透過 renderHook 這個方式:

React 18 後,renderHook 就可以直接從 @testing-library/react,原本的@testing-library/react-hook 可以說再見了,仍持續開發中

import { renderHook, act } from "@testing-library/react"

describe("Test with hook", () => {
  test("should default state be false", () => {
    const { result } = renderHook(() => useToggle())

    expect(result.current.isOn).toBe(false)
  })

  test("should default state be true from custom initial state", () => {
    const { result } = renderHook(() => useToggle({ defaultState: true }))

    expect(result.current.isOn).toBe(true)
  })

  test("should state toggle to true by toggle", () => {
    const { result } = renderHook(() => useToggle())

    act(() => result.current.toggle())

    expect(result.current.isOn).toBe(true)
  })

  test("should state toggle to true by toggleOn", () => {
    const { result } = renderHook(() => useToggle())

    act(() => result.current.toggleOn())

    expect(result.current.isOn).toBe(true)
  })

  test("should state toggle to false by toggleOff", () => {
    const { result } = renderHook(() => useToggle({ defaultState: true }))

    act(() => result.current.toggleOff())

    expect(result.current.isOn).toBe(false)
  })
})

定義了幾種情境:

  • 初始為 false
  • 初始為 true (透過設定初始值為true)
  • 透過方法 toggle 切換為 true
  • 透過方法 toggleOn 切換為 true
  • 透過方法 toggleOff 切換為 false

其中用了:

  • renderHook 由於 react 的 hook 使用限制,這個方法讓我們能夠在測試中使用,透過 result.current 可以取得原本 hook 回傳的內容
  • act 來執行動作

然後就可以測試啦!

由於是接在後面繼續寫,所以全部的測試都一起跑起來:

題外話:寫的過程不小心寫錯,錯誤訊息也顯示很清楚:

其實不太需要?

由於主題是跟 Hook 有關,回到測試 Hook 本身需不需要測試呢?

其實根據官方文件

  • When to use this library
    - You're writing a library with one or more custom hooks that are not directly tied to a component
    - You have a complex hook that is difficult to test through component interactions
  • When not to use this library
    - Your hook is defined alongside a component and is only used there
    - Your hook is easy to test by just testing the components using it

重點在如果可以直接透過元件輕易測試的畫,或是只用在特定的地方,就不需要刻意去寫測試了;反之,如果太過於複雜,難以透過元件來測試且使用於很多地方,就可以考慮。

結語

第一次寫測試好抽象,但也挺有趣的 (+_+)


上一篇
[DAY 27] 自己的Hook自己做!一些零碎的 hook 們
下一篇
[DAY 29] 自己的Hook自己發佈!
系列文
React Hook 不求人,建立自己的 Hook Libary30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言