为何Jest无法Mock React组件外部定义的QueryClient构造函数?
Jest Mock构造函数未被捕获的原因解析
问题描述
使用Jest Mock时遇到困惑:将QueryClient实例定义在React组件外部时,无法通过Spy捕获其构造函数的调用;但将实例创建逻辑移到组件内部时,就能正常捕获。示例代码如下:
import * as reactQuery from '@tanstack/react-query'; import { render } from '@testing-library/react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; // 此处定义client时,测试无法捕获构造函数调用 const client = new QueryClient(); const TestComponent = () => { // 移到此处定义时,测试正常工作 // const client = new QueryClient(); return ( <QueryClientProvider client={client}> <div>Some Component</div> </QueryClientProvider> ); }; jest.mock(`@tanstack/react-query`); it(`Test`, () => { const constructorSpy = jest.spyOn(reactQuery, `QueryClient`); render(<TestComponent />); expect(constructorSpy).toHaveBeenCalled(); // 外部定义时会报错:Expected number of calls: >= 1,Received number of calls: 0 });
原因解析
核心问题在于代码执行顺序与Spy的挂载时机:
- Jest会自动将
jest.mock()调用提升到模块代码的最顶部,所以模块加载时@tanstack/react-query确实已经被Mock。 - 当
new QueryClient()写在模块顶级(组件外部)时,这行代码会在模块加载阶段就执行,此时测试用例还未启动,jest.spyOn还未创建——也就是说,构造函数的调用发生在Spy挂载之前,自然不会被Spy统计到。 - 而将
new QueryClient()放在组件内部时,这行代码只会在测试用例执行时的render阶段运行,此时Spy已经创建完成,所以构造函数的调用会被正确捕获。
解决方案
如果需要在组件外部定义client,可以将实例化逻辑延迟到Spy创建之后:
import * as reactQuery from '@tanstack/react-query'; import { render } from '@testing-library/react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; let client; // 先声明变量,不初始化 const TestComponent = () => { return ( <QueryClientProvider client={client}> <div>Some Component</div> </QueryClientProvider> ); }; jest.mock(`@tanstack/react-query`); it(`Test`, () => { const constructorSpy = jest.spyOn(reactQuery, `QueryClient`); client = new QueryClient(); // 在Spy创建后再实例化client render(<TestComponent />); expect(constructorSpy).toHaveBeenCalled(); // 测试通过 });
内容的提问来源于stack exchange,提问作者gixlg
相关产品推荐
相关产品推荐

