创建Jest mock覆盖TableVirtuoso组件时报错如何解决?
Jest mock TableVirtuoso 触发构造函数报错修复
问题场景
测试用例中使用了react-virtuoso导出的TableVirtuoso组件,导入方式如下:
import { TableVirtuoso } from 'react-virtuoso';
为了在测试环境默认给组件注入initialItemCount属性(解决虚拟列表测试时不渲染子项的问题),尝试通过高阶组件包装原组件实现mock,最初编写的mock代码如下:
jest.mock('react-virtuoso', () => { const { TableVirtuoso } = jest.requireActual('react-virtuoso'); const mockVirtuoso = (WrappedVirtuoso: ElementType) => class extends mockComponent<{ totalCount: number }, unknown> { render() { return ( <WrappedVirtuoso initialItemCount={this.props?.totalCount} {...this.props} /> ); } }; return { TableVirtuoso: mockVirtuoso(TableVirtuoso) }; });
该实现参考了社区公开讨论的方案,但运行测试时抛出如下TypeError,无法正常执行用例:
TypeError: Class constructor cannot be invoked without 'new' 79 | wrapperOptions?: WrapperOptions, 80 | ) => > 81 | render(ui, { | ^ 82 | wrapper: props => <AllTheProviders {...props} {...wrapperOptions} />, 83 | ...options, 84 | });
报错原因
报错核心是两点:
- 自定义的
mockComponent基类没有正确实现ES类构造逻辑,在React渲染调用时没有走new实例化流程 - Jest的mock模块提升机制会把
jest.mock工厂函数提前到文件顶部执行,在类继承场景下容易拿到未初始化的真实组件引用,触发构造规则校验失败
修复代码
直接放弃类继承的HOC实现,改用函数组件包装原组件,同时透传原组件的所有静态属性,避免内置方法丢失:
jest.mock('react-virtuoso', () => { const React = require('react'); const actualLib = jest.requireActual('react-virtuoso'); const OriginalTableVirtuoso = actualLib.TableVirtuoso; const MockedTableVirtuoso = (props) => { return ( <OriginalTableVirtuoso initialItemCount={props.totalCount} {...props} /> ); }; // 透传原组件所有静态属性,比如scrollToIndex等内置方法 Object.assign(MockedTableVirtuoso, OriginalTableVirtuoso); return { ...actualLib, TableVirtuoso: MockedTableVirtuoso, }; });
注意事项
- 如果项目使用的是React 17+的新JSX转换,不需要在文件顶部导入React也可以正常写JSX
- 如果只需要mock TableVirtuoso,其他导出项保持原有实现,一定要把真实库的其他导出项一起返回,避免其他用到库内方法的用例报错
- 不要在mock工厂外部提前引用真实组件,否则会因为jest的mock提升机制拿到undefined引用
内容的提问来源于stack exchange,提问作者Renaud is Not Bill Gates
相关产品推荐
相关产品推荐

