Jest中挂载React组件后如何调用其内部函数?
问题背景
我有一个React组件,代码结构如下:
class HomePageGrid extends React.Component { // 组件内部包含moveUp方法 moveUp() { // 处理state逻辑 this.setState({ cardCurrentFocus: this.state.cardCurrentFocus - 1 }); } // 其他组件代码 } export default StateHoc(HomePageGrid); export {HomePageGrid as PureHomePageGrid};
我编写了如下测试用例,想要验证moveUp方法对组件state的修改:
it("Checks whether state changes on moveUp", () => { const wrapper = mount(<PureHomePageGrid />); var inst = wrapper.instance(); inst.moveUp(); expect(inst.state.cardCurrentFocus).toBe(0); });
但运行测试时遇到错误:inst.moveUp is not a function。尝试使用wrapper.dive().instance()时,又报错wrapper.dive is not a function,提示dive仅适用于shallow渲染。请问在Jest中挂载(mount)组件后,如何正确调用组件的实例函数?
解决思路与方案
1. 先确认组件方法的定义与绑定是否正确
首先要排查你的HomePageGrid类中,moveUp方法是否是正确的实例方法,并且this绑定没有问题。常见的错误是方法未绑定导致调用时this指向错误,或者方法定义成了静态方法。
正确的写法有两种:
- 使用箭头函数自动绑定
this:
class HomePageGrid extends React.Component { moveUp = () => { this.setState(prevState => ({ cardCurrentFocus: prevState.cardCurrentFocus - 1 })); } }
- 在构造函数中手动绑定
this:
class HomePageGrid extends React.Component { constructor(props) { super(props); this.moveUp = this.moveUp.bind(this); } moveUp() { this.setState(prevState => ({ cardCurrentFocus: prevState.cardCurrentFocus - 1 })); } }
如果方法没有正确绑定,就会出现is not a function的错误。
2. 针对mount渲染的组件,直接使用wrapper.instance()获取实例
当你用mount渲染未被HOC包装的PureHomePageGrid时,wrapper.instance()应该能直接拿到组件的实例。如果还是报错,可以先打印实例内容,确认是否是HomePageGrid的实例:
it("Checks whether state changes on moveUp", () => { const wrapper = mount(<PureHomePageGrid />); const inst = wrapper.instance(); // 打印实例,确认是否包含moveUp方法 console.log(inst); inst.moveUp(); // 调用方法后更新wrapper,确保state同步 wrapper.update(); expect(wrapper.state().cardCurrentFocus).toBe(0); });
注意调用方法后要调用wrapper.update(),保证wrapper的状态和组件实例的状态一致。
3. 理清dive()的适用场景
dive()是Enzyme中shallow渲染特有的方法,用来深入一层渲染被高阶组件(HOC)包装的内部组件。如果你测试的是被StateHoc包装后的默认导出组件,才需要用shallow+dive():
// 测试被HOC包装后的组件 it("Checks whether state changes on moveUp in wrapped component", () => { import HomePageGrid from './HomePageGrid'; // 导入HOC包装后的组件 const wrapper = shallow(<HomePageGrid />); const inst = wrapper.dive().instance(); inst.moveUp(); wrapper.update(); expect(inst.state.cardCurrentFocus).toBe(0); });
但你测试的是未被包装的PureHomePageGrid,所以mount渲染后不需要使用dive()。
4. 检查Enzyme与React的版本兼容性
不同版本的Enzyme和React的API可能存在差异,比如Enzyme 3.x+中,mount返回的wrapper获取实例的方式是wrapper.instance(),而如果是函数组件则返回null(你的组件是类组件,所以不受影响)。如果你的Enzyme版本过旧,可能会出现API不一致的问题,建议确保Enzyme和React版本匹配(比如Enzyme 3.x对应React 16.x,Enzyme 4.x对应React 17.x等)。
5. 替代方案:通过触发事件间接调用方法
如果直接调用实例方法还是有问题,可以考虑通过触发组件的交互事件(比如按钮点击)来间接调用moveUp,这样更贴近真实用户场景:
it("Checks whether state changes on moveUp via button click", () => { const wrapper = mount(<PureHomePageGrid />); // 假设组件中有一个触发moveUp的按钮,通过选择器找到它 wrapper.find('#move-up-btn').simulate('click'); wrapper.update(); expect(wrapper.state().cardCurrentFocus).toBe(0); });
内容的提问来源于stack exchange,提问作者prasadmsvs

