如何测试包含异步代码的自定义React Hook?
问题:测试自定义异步Hook时,始终得到
[null]结果,无法等待fetch完成 我写了一个通过GET请求获取数据的自定义Hook,尝试测试它时,实际得到的响应是包含null的数组[null],测试失败。
自定义Hook代码
import { useState, useEffect } from "react"; export const useData = (url) => { const [data, setData] = useState(null); useEffect(() => { fetch(url) .then((res) => res.json()) .then((data) => setData(data)); }, [url]); return [data]; };
测试代码
import { renderHook, waitFor } from "@testing-library/react"; import { useData } from "./useData"; test("fetching the jsonarray", async () => { const { result } = renderHook(() => useData("https://jsonplaceholder.typicode.com/todos?_limit=1") ); let actualResponse; await waitFor(() => { actualResponse = result.current; }); expect(actualResponse).toEqual([ { userId: 1, id: 1, title: "delectus aut autem", completed: false, }, ]); });
测试失败结果
Expected - 6 + Received + 1 Array [ - Object { - "completed": false, - "id": 1, - "title": "delectus aut autem", - "userId": 1, - }, + null, ]
解决方法
1. 给waitFor添加等待断言条件
你当前的waitFor只是赋值操作,没有设置等待的终止条件,所以它会立即执行,直接拿到Hook初始的[null]结果就结束了。需要让waitFor等待数据不为null:
修改测试代码:
test("fetching the jsonarray", async () => { const { result } = renderHook(() => useData("https://jsonplaceholder.typicode.com/todos?_limit=1") ); // 等待数据加载完成(不为null) await waitFor(() => { expect(result.current[0]).not.toBeNull(); }); // 此时再获取结果进行断言 const actualResponse = result.current; expect(actualResponse).toEqual([ { userId: 1, id: 1, title: "delectus aut autem", completed: false, }, ]); });
2. Mock fetch请求(推荐)
真实网络请求受环境影响,测试稳定性差,建议用Jest模拟fetch,隔离外部依赖:
import { renderHook, waitFor } from "@testing-library/react"; import { useData } from "./useData"; // 测试前模拟fetch beforeEach(() => { global.fetch = jest.fn(() => Promise.resolve({ json: () => Promise.resolve({ userId: 1, id: 1, title: "delectus aut autem", completed: false, }), }) ); }); // 测试后恢复原fetch afterEach(() => { jest.restoreAllMocks(); }); test("fetching the jsonarray", async () => { const { result } = renderHook(() => useData("任意URL")); await waitFor(() => { expect(result.current[0]).not.toBeNull(); }); expect(result.current).toEqual([ { userId: 1, id: 1, title: "delectus aut autem", completed: false, }, ]); // 还可以断言fetch被调用过 expect(fetch).toHaveBeenCalledTimes(1); });
3. 给Hook添加错误处理(可选)
原Hook没有错误捕获逻辑,若请求失败会导致测试超时或卡住。可以优化Hook:
import { useState, useEffect } from "react"; export const useData = (url) => { const [data, setData] = useState(null); const [error, setError] = useState(null); useEffect(() => { fetch(url) .then((res) => { // 检查响应状态 if (!res.ok) throw new Error(`请求失败:${res.status}`); return res.json(); }) .then((data) => setData(data)) .catch((err) => setError(err.message)); }, [url]); return [data, error]; };
测试时也可以断言错误状态,让测试覆盖更多场景。
内容的提问来源于stack exchange,提问作者Gautham Muralidharan
相关产品推荐
相关产品推荐

