Enzyme如何测试React组件本地状态是否更新?代码问题求助
问题:React组件本地状态更新的单元测试失败
我正在测试下拉框选项选中时触发的函数,该函数会在ListOfTemplates列表变化时更新组件本地state的value字段。但测试时发现调用onChange(2)后,组件state的value仍为初始值1,而非预期的2。请问如何正确测试本地状态是否更新?我的代码哪里出错了?
容器组件代码
import React from 'react'; import { connect } from 'react-redux'; import {bindActionCreators} from 'redux'; import * as getData from '../../services/DataService'; class Container extends React.Component { constructor(props){ super(props) this.props.actions.getTemplates(1); this.state = { value: 1 } } handlelistOfTemplates = (value, index) => { this.setState({ value }); }; componentDidMount() { } render() { return( <ListOfTemplates listOfTemplates={this.props.listOfTemplates} value={this.state.value} onChange={this.handlelistOfTemplates} /> ); } } function mapStateToProps({state}) { return { listOfTemplates: state.listOfTemplates } } function mapDispatchToProps(dispatch) { return { actions: bindActionCreators(getData, dispatch) }; } module.exports = connect(mapStateToProps, mapDispatchToProps)(Container);
测试代码
import React from 'react'; import sinon from 'sinon'; import expect from 'expect'; import { shallow } from 'enzyme'; import PropTypes from 'prop-types'; import getMuiTheme from 'material-ui/styles/getMuiTheme'; import { createMockStore, createMockDispatch } from 'redux-test-utils'; import Container from './Container'; const shallowWithStore = (component, store) => { const context = { store, muiTheme: getMuiTheme(), }; const childContextTypes = { muiTheme: React.PropTypes.object.isRequired, store: PropTypes.object.isRequired } return shallow(component, { context, childContextTypes }); }; let store; const loadComponent = (testState) => { const props = { actions: { getTemplates: () => {return Promise.resolve()} } } store = createMockStore(testState) return shallowWithStore(<Container {...props}/>, store); } const getFakeState = () => { return { listOfTemplates: [], }; } export default shallowWithStore; describe('Container', () => { let testState, component; describe("when Appeal template is selected from select template dropdown", () => { beforeAll(() => { testState = getFakeState(); component = loadComponent(testState); }); fit('should update the content in editor', (done) => { component.dive().find('ListOfTemplates').props().onChange(2); component.update(); expect(component.dive().state().value).toEqual(2); // 实际返回1而非预期的2 done(); }); }); });
问题分析与解决方案
核心问题:多次dive()导致操作不同组件实例
你每次调用component.dive()都会生成一个全新的浅渲染组件实例。第一次dive()调用onChange时,更新的是这个临时实例的状态,但第二次dive()又重新创建了一个初始状态的实例,所以断言时拿到的还是初始值1。
修复步骤
- 复用
dive()后的实例:把component.dive()的结果存储到变量中,后续的事件触发和状态断言都使用这个同一个实例。 - 处理
setState的异步性:setState是异步操作,调用后需要用update()强制组件同步更新状态,再进行断言。
修改后的测试代码
fit('should update the content in editor', () => { // 保存dive后的组件实例,后续操作都基于它 const containerInstance = component.dive(); // 触发下拉框的onChange事件 containerInstance.find('ListOfTemplates').props().onChange(2); // 强制组件同步更新状态 containerInstance.update(); // 断言同一个实例的状态值 expect(containerInstance.state().value).toEqual(2); });
额外提示
- 你的测试中原本使用了
done回调,但当前场景没有异步逻辑(除非handlelistOfTemplates内部有异步操作),可以直接去掉,让测试同步执行。 - 如果后续遇到涉及异步状态更新的测试,可以结合
async/await或者setState的回调函数来确保状态更新完成后再断言。
内容的提问来源于stack exchange,提问作者Sinha
相关产品推荐
相关产品推荐

