Testing React Query
關於 React Query 的測試問題經常被問到,這裡我會嘗試解答一些常見疑問。我認為其中一個原因是測試「聰明」元件(也叫 container components)本來就不容易。隨著 hooks 的普及,這種分法已經不再流行,現在建議直接在需要的地方用 hook,而不是硬把元件拆成 dumb/props-only。
這樣的確讓元件更好維護、程式碼更易讀,但也讓更多元件會用到「非 props」的外部依賴。
可能會用 useContext
、useSelector
,或 useQuery
。
這些元件技術上已經不是純元件,因為在不同環境下呼叫會有不同結果。測試時你必須小心建 立正確的環境,才能讓測試順利運作。
模擬網路請求
React Query 是個非同步 server state 管理函式庫,你的元件很可能會發送請求到後端。測試時後端通常不可用,就算可用,也不希望測試依賴真實後端。
網路 mock 方式很多,像 jest mock api client、mock fetch 或 axios。我很推薦 Kent C. Dodds 的這篇文章:
它可以成為你 mock api 的唯一真相來源:
- node 環境下可用於測試
- 支援 REST 與 GraphQL
- 有 storybook addon,可寫
useQuery
元件 story - 開發時在瀏覽器也能用,且能在 devtools 看到請求
- 跟 cypress 結合也很方便
網路層搞定後,來談談 React Query 測試要注意的事:
QueryClientProvider
用 React Query 時一定要有 QueryClientProvider,並給它一個 queryClient(裡面有 QueryCache
,會存查詢資料)。
我建議每個測試都建立自己的 QueryClientProvider,並用 new QueryClient
。這樣測試彼此完全隔離。另一種做法是每次測試後清空 cache,但我偏好讓測試間的共享狀態越少越好。不然平行跑測試時很容易出現莫名其妙的錯誤。
測 custom hook
如果你在測自訂 hook,應該會用 react-hooks-testing-library。這是測 hook 最簡單的工具。它可以用 wrapper 包住 hook,這個 wrapper 就是 React component。這裡很適合建立 QueryClient,因為每個測試都會執行一次:
// wrapper
const createWrapper = () => {
// ✅ 每個測試都建立新 QueryClient
const queryClient = new QueryClient()
return ({ children }) => (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
)
}
test('my first test', async () => {
const { result } = renderHook(() => useCustomHook(), {
wrapper: createWrapper(),
})
})
測元件
如果你要測用 useQuery
的元件,也要用 QueryClientProvider 包住。可以寫個小 wrapper 包住 react-testing-library 的 render。可以參考 React Query 官方測試的做法。