Next.js Jest RTL环境下如何监听useRouter的replace mock函数调用?
问题根因
当前的mock逻辑中,每次调用useRouter()都会返回一个全新的对象,你在测试代码里调用useRouter()通过spyOn监听的replace方法,和组件内部调用useRouter()拿到的replace是完全独立的两个mock实例。组件实际调用的是内部拿到的那个实例,所以会打印日志,但你监听的spy实例从未被调用,因此断言失败。
解决方案
将mock的replace方法提取为公共的单实例mock函数,保证所有useRouter()返回的对象都复用同一个replace实例,直接断言这个公共mock即可,不需要额外用spyOn:
- 修改mock代码:
// 定义全局共享的replace mock函数 const mockReplace = jest.fn((obj) => console.log("have been called", obj)); jest.mock("next/router", () => ({ useRouter() { return { route: "/", pathname: "", locale: "pl", replace: mockReplace, // 所有router实例共用同一个replace mock }; }, }));
- 修改测试代码:
test.only("should redirect when form submitted with success", async () => { // 每次测试前清空mock的调用记录,避免用例间互相干扰 mockReplace.mockClear(); render(<HomePage {...props} />); const submitBtn = screen.getByRole("button", { name: /submit/i, }); // userEvent操作本身是异步的,建议加await await userEvent.click(submitBtn); await waitFor(() => expect(mockReplace).toHaveBeenCalled()); // 也可以直接判断调用次数 // expect(mockReplace).toHaveBeenCalledTimes(1); });
注意事项
- 不要在测试代码中反复调用
useRouter()获取方法,默认mock逻辑下每次返回的都是全新实例,和组件内部使用的实例不共用 - 多个测试用例共用同一个mock函数时,一定要在每个用例执行前调用
mockClear()清空历史调用记录,避免用例结果互相影响
内容的提问来源于stack exchange,提问作者goscamp
相关产品推荐
相关产品推荐

