如何用Jest测试React组件内部定义的onClick事件?
如何测试React组件内部定义的onClick事件?
我找到了很多在Jest中模拟监视传递给组件的回调函数的方法,但找不到测试组件内部定义的简单onClick事件的方案。
示例页面代码
const ExamplePage: NextPage = () => { const router = useRouter(); const onClick = (): Promise<void> => { axios.post(`/api/track`, { eventName: Event.TRACK_CLICK, }); router.push("/new-route"); return Promise.resolve(); }; return ( <Container data-testid="container"> <Title>Example Title</Title> <CreateButton data-testid="create-button" onClick={onClick}> Create Partner </CreateButton> </Container> ); }; export default ExamplePage;
我尝试的测试代码(失败)
import { fireEvent, render } from "../../../../test/customRenderer"; import ExamplePage from "../../../pages/example-page"; describe("Example page", () => { it("has a button to create", () => { const { getByTestId } = render(<ExamplePage />); const createButton = getByTestId("create-button"); expect(createButton).toBeInTheDocument(); }); it(" the button's OnClick function should be executed when clicked", () => { const { getByTestId } = render(<ExamplePage />); // find the button const createButton = getByTestId("create-button"); // check the button has onClick expect(createButton).toHaveAttribute("onClick"); // get the onClick function const onClick = createButton.getAttribute("onClick"); fireEvent.click(createButton); // check if the button's onClick function has been executed expect(onClick).toHaveBeenCalled(); }); });
上面的测试失败了,因为通过getAttribute获取到的onClick属性值是null。我的思路是获取按钮上的函数并检查它是否被调用,但这个方法行不通。
解决方案
核心思路:测试函数的副作用,而非函数本身
React不会将组件内部定义的函数暴露为DOM元素的属性,所以你没法通过DOM节点直接拿到onClick函数并断言它是否被调用。正确的做法是测试点击按钮后产生的实际业务效果——也就是onClick函数执行时触发的副作用,比如API请求、路由跳转、状态变更等。
具体实现:模拟依赖并验证调用
我们需要把组件依赖的axios和useRouter模拟掉,通过验证这些依赖是否被正确调用,来间接证明onClick函数执行了预期逻辑。
修改后的测试代码如下:
import { fireEvent, render } from "../../../../test/customRenderer"; import ExamplePage from "../../../pages/example-page"; import axios from "axios"; import { useRouter } from "next/router"; import { Event } from "./path-to-event-constants"; // 导入你的Event常量 // 全局模拟axios和next/router jest.mock("axios"); jest.mock("next/router", () => ({ useRouter: jest.fn(), })); describe("Example page", () => { beforeEach(() => { // 每次测试前重置模拟函数,避免测试间的污染 (axios.post as jest.Mock).mockClear(); // 模拟router.push方法 const mockPush = jest.fn(); (useRouter as jest.Mock).mockReturnValue({ push: mockPush, }); }); it("点击按钮后会发送追踪请求并跳转到指定路由", () => { const { getByTestId } = render(<ExamplePage />); const createButton = getByTestId("create-button"); // 触发按钮点击 fireEvent.click(createButton); // 验证axios是否发送了正确的POST请求 expect(axios.post).toHaveBeenCalledWith("/api/track", { eventName: Event.TRACK_CLICK, }); // 验证路由跳转是否被触发 const router = useRouter() as ReturnType<typeof useRouter>; expect(router.push).toHaveBeenCalledWith("/new-route"); }); });
补充说明
- 如果你的
onClick函数包含状态更新逻辑(比如修改组件内的useState值),可以通过查询组件渲染后的UI变化来验证(比如检查某个元素是否显示、文本是否更新等)。 - 这种测试方式更贴近用户实际使用场景,我们不需要关心函数内部的实现细节,只需要确保功能符合预期即可。
内容的提问来源于stack exchange,提问作者lpritchard
相关产品推荐
相关产品推荐

