Jest+testing-library如何动态Mock组件解决作用域报错
报错原因
Jest 执行测试时默认会把所有jest.mock()调用自动提升到当前文件最顶部,执行优先级高于所有import语句、变量声明和函数参数定义。你封装的mockComponent函数里传入的source、name属于函数作用域内的参数,在jest.mock被提升执行的时机还未完成初始化,工厂函数访问不到这些外部变量,就会抛出越权访问的错误。
可行解决方案
根据使用场景选对应方案即可:
方案1:封装通用动态mock函数(适配抽离公共逻辑的需求)
用不会被自动提升的jest.doMock替代jest.mock,同时配合模块缓存重置,保证mock结果生效,支持完全动态的参数传入:
export function mockComponent(source: string, name: string) { jest.doMock(source, () => ({ // 保留模块原有其他导出,避免影响其他组件的正常使用 ...jest.requireActual(source), [name]: (props: any) => { const ComponentMock = `${name}-mock`; // @ts-ignore return <ComponentMock {...props} />; }, })); // 清除对应模块的缓存,保证后续导入时能拿到mock后的版本 jest.resetModules(); }
注意事项:
- 该函数必须在第一次导入被mock的模块之前调用,如果之前已经导入过模块,重置缓存后需要重新导入才能拿到mock结果。
- 如果需要mock某个模块的多个导出组件,重复调用该函数传不同的
name即可。
方案2:单文件内批量mock(无需封装工具的轻量场景)
Jest的mock工厂函数默认允许访问名字以mock开头的外部变量,可以直接在文件顶层用mock前缀的变量存要mock的模块名、组件名,不需要额外封装函数:
const mockRouterSource = 'react-router-dom'; const mockNavLinkName = 'NavLink'; jest.mock(mockRouterSource, () => ({ ...jest.requireActual(mockRouterSource), [mockNavLinkName]: (props: any) => { const NavLinkMock = 'NavLink-Mock'; // @ts-ignore return <NavLinkMock {...props} />; } }));
方案3:全局公共mock(适配react-router-dom这类全项目通用依赖)
对于多个测试文件都要用到的公共依赖mock,不需要在每个文件写重复逻辑,直接在项目内建__mocks__目录维护固定mock实现即可:
- 在项目根目录新建
__mocks__/react-router-dom.ts文件 - 写入固定mock逻辑:
const actualRouter = jest.requireActual('react-router-dom'); module.exports = { ...actualRouter, NavLink: (props: any) => { const NavLinkMock = 'NavLink-Mock'; // @ts-ignore return <NavLinkMock {...props} />; } }
- 所有测试文件里只要写一行
jest.mock('react-router-dom'),Jest就会自动加载你写好的全局mock,不需要重复写实现。
内容的提问来源于stack exchange,提问作者Teehbow
相关产品推荐
相关产品推荐

