升级React 16与Enzyme 3后测试调用setState失败求助
嘿,我帮你排查出这个测试失败的问题了,主要是两个核心原因在搞鬼,咱们一步步来解决:
问题根源
- 组件
onClick方法的this指向错误:你在组件render里用箭头函数调用this.onClick(),但onClick是Class方法,默认不会自动绑定this。React的DEV模式(测试环境默认启用)下开启了严格模式,这会让Class方法中的this变成undefined,导致调用this.setState时抛出Cannot read property 'setState' of undefined错误——只是React的错误提示没有直接显示这个具体信息,而是给出了那个通用的模糊提示。 - 测试事件触发方式不符合Enzyme 3.x + React 16的最佳实践:直接调用
maskedElem.prop('onClick')()会绕过React的合成事件系统,不仅模拟不真实,还会导致错误无法被正确捕获和提示。
解决步骤
第一步:修复组件的this绑定问题
你可以选以下三种方式中的任意一种,确保onClick方法里的this指向组件实例:
方式一:构造函数内手动绑定
这是最传统也最稳妥的方式,不需要额外Babel配置:
class Phone extends React.Component { constructor(props) { super(props); this.state = { clicked: false }; this.onClick = this.onClick.bind(this); // 新增这行绑定this } onClick() { this.setState({ ...this.state, clicked: true }) } render() { // 其余代码保持不变 } }
方式二:使用Class箭头函数(需Babel支持Class Properties)
这种写法更简洁,箭头函数会自动绑定当前组件实例的this:
class Phone extends React.Component { constructor(props) { super(props); this.state = { clicked: false }; } // 用箭头函数定义方法,自动绑定this onClick = () => { this.setState({ ...this.state, clicked: true }) } render() { // 其余代码保持不变 } }
方式三:直接在render的事件中写逻辑(适合简单场景)
如果你的点击逻辑很简单,也可以省去单独的onClick方法,直接在事件回调里写逻辑:
class Phone extends React.Component { constructor(props) { super(props); this.state = { clicked: false }; } render() { if (this.state.clicked) { return ( <span>{this.props.value}</span> ); } else { return ( <span onClick={() => this.setState({ clicked: true })} className='masked'> {MASKED} </span> ); } } }
第二步:修改测试用例的事件触发方式
在Enzyme 3.x中,推荐用simulate方法来触发DOM事件,这能更真实地模拟浏览器的事件流程,避免绕过React事件系统的问题:
it('should show real numbers on click', () => { // 初始状态显示掩码值 expect(wrapper).to.contain.text(MASKED); // 用simulate触发点击,替代直接调用prop('onClick')() const maskedElem = wrapper.find('.masked'); maskedElem.simulate('click'); // 期望显示真实值 expect(wrapper).to.contain.text(VALUE); });
验证
完成以上两步修改后,重新运行测试用例,应该就能正常通过了。
内容的提问来源于stack exchange,提问作者TrongBang
相关产品推荐
相关产品推荐

