如何在不传入初始值的情况下测试内部带useState的ModalProvider?
无需传入初始值即可测试内部维护状态的ModalContext
问题根源
你在测试代码的顶层作用域直接调用useModalContext(),此时ModalProvider还未被渲染到测试DOM中,上下文完全未生效,必然触发hook里的错误提示。React Hooks的调用规则要求:必须在组件(或被render包裹的组件树)内部、且处于上下文Provider的覆盖范围内调用。
解决方案(无需修改ModalProvider)
下面两种方案都不需要给ModalProvider添加initialValue参数,完全保留原有的内部状态逻辑:
方案1:将上下文逻辑移到测试组件内部
把需要使用useModalContext的交互逻辑(比如按钮点击触发模态框)放到一个测试专用的组件里,确保hook在ModalProvider的上下文范围内被调用。
修改后的测试代码:
import React from "react"; import { render, screen, fireEvent } from "@testing-library/react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { ModalProvider, useModalContext } from "./ModalContext"; import Modal from "./Modal"; const queryClient = new QueryClient(); const Wrapper = ({ children }: { children: React.ReactNode }) => ( <ModalProvider> <QueryClientProvider client={queryClient}>{children}</QueryClientProvider> </ModalProvider> ); // 封装测试组件,在内部使用上下文 const TestModalTrigger = () => { const { setShowModal } = useModalContext(); return ( <> <button onClick={() => setShowModal(true)}>Show modal</button> <Modal /> </> ); }; describe("Modal test", () => { it("显示模态框当按钮被点击时", () => { render(<TestModalTrigger />, { wrapper: Wrapper }); // 找到按钮并点击 const showButton = screen.getByText("Show modal"); fireEvent.click(showButton); // 验证模态框内容存在 const modalContent = screen.getByText(/Show modal/i); expect(modalContent).toBeInTheDocument(); }); });
方案2:用renderHook单独测试上下文hook
如果需要单独测试useModalContext的状态逻辑,可以使用@testing-library/react提供的renderHook函数,将其包裹在ModalProvider中,确保hook能获取到上下文值:
import { renderHook, act } from "@testing-library/react"; import { ModalProvider, useModalContext } from "./ModalContext"; describe("useModalContext Hook", () => { it("可以切换模态框显示状态", () => { const { result } = renderHook(() => useModalContext(), { wrapper: ({ children }) => <ModalProvider>{children}</ModalProvider> }); // 初始状态为隐藏 expect(result.current.showModal).toBe(false); // 触发显示 act(() => { result.current.setShowModal(true); }); expect(result.current.showModal).toBe(true); // 触发隐藏 act(() => { result.current.setShowModal(false); }); expect(result.current.showModal).toBe(false); }); });
内容的提问来源于stack exchange,提问作者asus
相关产品推荐
相关产品推荐

