如何使用Jest和React Testing Library Mock React子组件
Jest 同模块子组件Mock方案
报错根因
你当前写法报错的核心原因有两个:
Tree和Child定义在同一个模块文件中,Tree内部默认调用的是模块内部作用域的Child函数,你通过jest.doMock修改的只是模块的导出值,并不会影响模块内部的变量引用,所以Tree渲染时无法读取到mock后的Child。- 测试顶部提前导入了
Tree组件,模块已经被加载进Jest缓存,后续的doMock动态修改不会覆盖已缓存的模块内容。
推荐解决方案:拆分组件到独立模块
这是最符合项目开发规范的方案,拆分后单独mock子组件所属模块即可:
第一步:拆分组件文件
// ./features/test-samples/Child.js export const Child = () => { return <h1>Child</h1> }
// ./features/test-samples/tree.js import { Child } from './Child' export const Tree = () => { return (<> <h1>Tree</h1> <Child/> </>) }
第二步:编写测试用例
import { render as rtlRender } from '@testing-library/react'; // 每次测试前清除模块缓存,避免用例间互相影响 beforeEach(() => { jest.resetModules() }) describe.only('Test <Tree/> component', () => { test("test mock child", () => { // mock子组件模块,必须放在require父组件之前执行 jest.mock('./features/test-samples/Child', () => ({ Child: jest.fn(() => <div>Mocked Child</div>) })) // 动态导入父组件 const { Tree } = require('./features/test-samples/tree') const { getByText } = rtlRender(<Tree />) expect(getByText(/Tree/i)).toBeInTheDocument() expect(getByText(/Mocked Child/i)).toBeInTheDocument() }); })
兼容方案:不拆分模块的修改方式
如果你一定要保留两个组件在同一个文件中,可以修改Tree的引用逻辑,让它读取模块导出的Child而不是内部变量:
修改原组件
// ./features/test-samples/tree.js export const Child = () => { return ( <h1>Child</h1> ) } export const Tree = () => { return (<> <h1>Tree</h1> {/* 引用模块导出的Child,mock修改导出值后会生效 */} <module.exports.Child/> </>) }
测试用例即可正常运行
你原来的测试写法加上清除缓存的逻辑就可以正常执行:
beforeEach(() => { jest.resetModules() }) test("test mock child", () => { const actualModule = jest.requireActual('./features/test-samples/tree'); jest.doMock('./features/test-samples/tree', () => { return { ...actualModule, Child: jest.fn(() => <div>Mocked Child</div>), } }); const { Tree } = require('./features/test-samples/tree'); const { getByText } = rtlRender(<Tree />); expect(getByText(/Mocked Child/i)).toBeInTheDocument(); });
内容的提问来源于stack exchange,提问作者Laurence Fass
相关产品推荐
相关产品推荐

