React单元测试中数组filter+includes失效问题排查
问题:Primereact Column Toggler的onColumnToggle测试时状态未正确更新
我来帮你拆解下这个问题——你遇到的核心卡点是JavaScript对象引用的严格相等判断导致的,咱们一步步理清楚:
你的组件核心代码
constructor() { super(); this.state = { selectedColumns: [] } } componentDidMount() { this.setState({ selectedColumns: this.props.columnProps, }) } onColumnToggle(event) { let selectedColumns = event.value; let orderedSelectedColumns = this.props.columnProps.filter(obj => selectedColumns.includes(obj)); this.setState({selectedColumns: orderedSelectedColumns}); }
你的测试代码片段
describe('MyComponent', () => { let wrapper; let instance; beforeEach(() => { MyComponent.defaultProps = { columns: [ {field: 'vin', header: 'Vin'}, {field: 'year', header: 'Year'}, {field: 'brand', header: 'Brand'}, {field: 'color', header: 'Color'} ] }; wrapper = shallow(<MyComponent columnProps={MyComponent.defaultProps.columns}/>); instance = wrapper.instance(); jest.clearAllMocks(); }); describe('onColumnToggle function', () => { it('should change "selectedColumns" state', () => { const columnMock = { value: [ {field: 'year', header: 'Year'}, {field: 'brand', header: 'Brand'} ] } instance.onColumnToggle(columnMock); expect(wrapper.state('selectedColumns')).toEqual(columnMock.value) }) }) })
问题根源
你调试发现selectedColumns.includes(obj)始终返回false,本质原因是:
this.props.columnProps里的对象是你在beforeEach中定义的MyComponent.defaultProps.columns的内存实例- 而测试时
columnMock.value里的对象是全新创建的独立对象,虽然它们的字段和值完全一致,但JavaScript中对象是引用类型,includes内部用的严格相等(===)判断的是两个对象是否指向内存中的同一个地址,而非内容是否相同。
所以includes会判定这是两个不同的对象,返回false,最终filter得到空数组,state自然就变成了空数组。
解决方案
有两种实用的解决方式,你可以根据业务场景选择:
方案1:测试中复用props里的实际对象
既然组件依赖props里的对象引用,那测试时直接取用wrapper.props().columnProps里的对象,而非重新创建:
it('should change "selectedColumns" state', () => { const propsColumns = wrapper.props().columnProps; // 直接从props里取对应位置的对象引用 const columnMock = { value: [propsColumns[1], propsColumns[2]] // 对应year和brand的对象 } instance.onColumnToggle(columnMock); expect(wrapper.state('selectedColumns')).toEqual(columnMock.value) })
方案2:修改onColumnToggle函数,基于唯一标识判断(更健壮)
如果实际业务中,Primereact传递的event.value可能不是原props的对象引用(比如经过序列化/反序列化的数据),建议改用对象的唯一标识(比如field字段)来过滤,摆脱对对象引用的依赖:
onColumnToggle(event) { // 先提取选中列的唯一标识field const selectedFields = event.value.map(col => col.field); // 用field匹配过滤原props中的列 const orderedSelectedColumns = this.props.columnProps.filter(obj => selectedFields.includes(obj.field)); this.setState({selectedColumns: orderedSelectedColumns}); }
这种方式下,哪怕测试里传的是新对象,只要field匹配就能正确过滤,测试能通过的同时,代码的健壮性也更强。
内容的提问来源于stack exchange,提问作者User3000
相关产品推荐
相关产品推荐

