如何使用Jest模拟React组件内部的函数?
解决React函数组件内部私有函数无法被Jest spyOn模拟的问题
问题根源
你遇到的错误本质是:getServiceHistory是函数组件内部的私有闭包函数,既不是组件的实例方法,也没有对外导出。你尝试通过<RolloutPage/>(React元素对象)去spy这个函数,而这个元素对象里根本不存在getServiceHistory属性,自然会返回undefined导致报错。
可行解决方案
方案1:将函数提取为独立导出的工具函数
把getServiceHistory从组件内部抽离出来,单独导出,这样测试时可以直接mock这个函数:
组件代码修改:
// rolloutPage.js export const getServiceHistory = async (serviceName, account) => { return await fetch(/* 原有请求逻辑 */); }; const RolloutPage = () => { // 直接使用导出的函数 return <Page>{/* 组件原有内容 */}</Page>; }; export default withRouter(RolloutPage);
测试代码修改:
import { getServiceHistory } from './rolloutPage'; import RolloutPage from './rolloutPage'; test('测试组件业务逻辑', () => { jest.spyOn(getServiceHistory).mockImplementation(() => Promise.resolve({ json: () => Promise.resolve({ serviceTimelineDataMock }) }) ); render( <MemoryRouter> <RolloutPage /> </MemoryRouter> ); // 后续添加断言逻辑,比如验证数据渲染、函数调用情况等 });
方案2:直接Mock全局fetch(更推荐)
既然getServiceHistory的核心是调用fetch发起请求,直接mock全局的fetch API是更简洁的方式,无需修改组件代码:
测试代码:
import RolloutPage from './rolloutPage'; beforeEach(() => { // 全局mock fetch,模拟返回预期数据 global.fetch = jest.fn(() => Promise.resolve({ json: () => Promise.resolve({ serviceTimelineDataMock }) }) ); }); afterEach(() => { // 测试后恢复mock,避免影响其他测试用例 jest.restoreAllMocks(); }); test('测试组件业务逻辑', () => { render( <MemoryRouter> <RolloutPage /> </MemoryRouter> ); // 可额外断言fetch是否按预期调用 expect(fetch).toHaveBeenCalled(); });
方案3:通过ref暴露内部函数(不推荐)
如果必须要直接mock组件内部的getServiceHistory(不建议,会增加组件与测试的耦合度),可以用forwardRef和useImperativeHandle将函数暴露给外部:
组件代码修改:
import { forwardRef, useImperativeHandle } from 'react'; const RolloutPage = forwardRef((props, ref) => { const getServiceHistory = async (serviceName, account) => { return await fetch(/* 原有请求逻辑 */); }; // 仅暴露指定函数给外部ref useImperativeHandle(ref, () => ({ getServiceHistory })); return <Page>{/* 组件原有内容 */}</Page>; }); export default withRouter(RolloutPage);
测试代码修改:
import { render } from '@testing-library/react'; import { useRef } from 'react'; import RolloutPage from './rolloutPage'; test('测试组件业务逻辑', () => { const componentRef = useRef(null); render( <MemoryRouter> <RolloutPage ref={componentRef} /> </MemoryRouter> ); // 现在可以spy暴露出来的函数 jest.spyOn(componentRef.current, 'getServiceHistory').mockImplementation(() => Promise.resolve({ json: () => Promise.resolve({ serviceTimelineDataMock }) }) ); // 后续添加断言逻辑 });
内容的提问来源于stack exchange,提问作者Piazzi
相关产品推荐
相关产品推荐

