如何用Jest和React Testing Library测试自定义Hook内的函数
自定义React Hook测试问题解答
自定义Hook代码(useOpenTab.tsx)
import { useRouteParams } from "hooks"; import { useOpenTabMutation } from "state/modules/tab/api"; type FilteredData = any; export function useOpenTab() { const { assetId } = useRouteParams(); const [openTabAction] = useOpenTabMutation(); const openItems = (filteredData: FilteredData[]) => { const data = filteredData.reduce((acc, item) => { if ( acc.find( (order) => order.id === item.id && order.tag === item.location ) ) { return acc; } acc.push({ id: item.number, tag: item.location }); return acc; }, [] as { id: string; tag: string }[]); openTabAction({ assetId, data }); // 需要确认此处的"data"值 }; return openItems; }
测试代码(openTab.test.ts)
import { renderHook } from "@testing-library/react"; // eslint-disable-next-line import/no-named-as-default import { useRouteParams } from "hooks"; import * as useCustomHook from "../useOpenTab"; import { mockedData } from "./mockedData"; const resultAfterFunction = [ { id: "1", tag: "1", }, { id: "2", tag: "2", }, { id: "3", tag: "3", }, { id: "4", tag: "4", }, ]; // Mocking Selector hooks jest.mock("hooks"); const useRouteParamsMock = useRouteParams as jest.Mock; const openItemsMock = jest.fn(); jest .spyOn(useCustomHook, "useOpenTab") .mockImplementation(() => { return openItemsMock; // mocking the openItems function }); describe("data param of action should be correct", () => { beforeEach(() => { // mocking the useParams hook useRouteParamsMock.mockClear(); useRouteParamsMock.mockReturnValueOnce({ assetId: 1 }); }); it("should return an array with object inside", async () => { const { result } = renderHook(() => useCustomHook.useOpenTab() ); const openItems = result.current; openItems(mockedData); expect(openItemsMock).toHaveBeenLastCalledWith({ id: 1, data: resultAfterFunction, }); }); });
问题与解答
1. 目前的测试代码是否正确?
不正确。你完全mock了整个useOpenTab Hook,相当于没有测试原Hook里的openItems核心逻辑——包括reduce数据处理、调用openTabAction的逻辑,只是在测试自己写的openItemsMock函数,完全失去了测试的意义。
2. 如何监听openTabAction函数,查看传入的data参数值?
需要mock useOpenTabMutation(而不是整个Hook),让它返回一个被jest监听的mock函数,然后直接测试原Hook的逻辑:
import { renderHook } from "@testing-library/react"; import { useRouteParams } from "hooks"; import { useOpenTabMutation } from "state/modules/tab/api"; import { useOpenTab } from "../useOpenTab"; import { mockedData } from "./mockedData"; // 预期的reduce处理后的数据 const expectedData = [ { id: "1", tag: "1" }, { id: "2", tag: "2" }, { id: "3", tag: "3" }, { id: "4", tag: "4" }, ]; // mock依赖的hooks jest.mock("hooks", () => ({ useRouteParams: jest.fn(), })); jest.mock("state/modules/tab/api", () => ({ useOpenTabMutation: jest.fn(), })); describe("useOpenTab - openItems function", () => { const mockOpenTabAction = jest.fn(); beforeEach(() => { // 重置所有mock jest.clearAllMocks(); // mock useRouteParams返回固定assetId (useRouteParams as jest.Mock).mockReturnValue({ assetId: 1 }); // mock useOpenTabMutation返回mock的action函数 (useOpenTabMutation as jest.Mock).mockReturnValue([mockOpenTabAction]); }); it("should process filteredData correctly and call openTabAction with right params", () => { // 渲染原Hook const { result } = renderHook(() => useOpenTab()); // 拿到openItems函数并调用 result.current(mockedData); // 断言openTabAction被调用,且参数正确 expect(mockOpenTabAction).toHaveBeenCalledWith({ assetId: 1, data: expectedData, }); }); });
3. 解决jest.spyOn的Cannot redefine property错误
这个错误是因为你试图spyOn一个直接导出的函数,jest默认无法重新定义这类属性。解决方式:
- 放弃spyOn整个
useOpenTab,转而mock它的依赖(useRouteParams和useOpenTabMutation),直接测试原Hook的真实逻辑(这也是正确的测试思路) - 如果你一定要spyOn,需要将导出改为
let声明或配置jest允许重新定义属性,但前者更符合测试规范,同时也能解决错误。
内容的提问来源于stack exchange,提问作者meerkat
相关产品推荐
相关产品推荐

