雖然寫了那麼多,不論是有用的,或沒用的,要怎麼確保有沒有寫好呢?
當然就是就在講測試,雖然開發過程中可以測試功能正不正常,但隨著日後長大的需求有時難以回頭一一確認,測試就是為此而來!
但工作一陣子一直沒機會接觸(真的慚愧),就藉鐵人賽這個機會來認識看看!
如果是用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+!)
那當然就需要先來寫一個元件:
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()
})
})
這邊定義好了三種使用情境:
而我們透過:
render
來渲染出真實的DOM來互動screen.getBy...
來抓出需要的元素,透過 expect
來期望有無正確顯示,沒有就會報錯userEvent
來模擬 click
event然後就可以跑起來:
npm run test
結果:
若不搭配元件,想直接測試 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)
})
})
定義了幾種情境:
其中用了:
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
重點在如果可以直接透過元件輕易測試的畫,或是只用在特定的地方,就不需要刻意去寫測試了;反之,如果太過於複雜,難以透過元件來測試且使用於很多地方,就可以考慮。
第一次寫測試好抽象,但也挺有趣的 (+_+)