我想整天解决这个问题,但无法正常工作。
我有一个用于更新componentDidMount
内部状态的组件,然后在setState's
回调中,我再次更新状态,但是回调异步运行。这是因为我在其中发出一个http请求,并使用其响应来更新状态。在单元测试期间,我模拟了此模块,因此它无法到达网络。但是,酶不会在异步回调中反映状态更改。这是有道理的,因为它是异步的,但是我还没有找到解决方法。
我已经建立了我想要达到的目标的缩小版本。
这是我的组件:
class Component extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 0,
};
this._isMounted = false;
}
componentDidMount() {
this._isMounted = true;
this.setState(state => {
const { value } = this.state;
return {
...state,
value: value + 15,
};
}, async () => {
try {
// If you remove this line, it works.
await Promise.resolve(15);
if (this._isMounted) {
this.setState(state => {
const { value } = this.state;
// If you console.log during testing,
// this value is updated correctly, but Enzyme doesn't reflect it.
return {
...state,
value: value + 15,
};
});
}
} catch(e) {
// Do nothing...
}
});
}
componentWillUnmount() {
this._isMounted = false;
}
render() {
const { value } = this.state;
return (
<div>{ value } </div>
);
}
}
这是我的考验:
describe('Component Enzyme Bug Testing', () => {
it('should update value properly', () => {
const wrapper = shallow(<Component />, { disableLifecycleMethods: true });
wrapper.instance().componentDidMount();
expect(wrapper.state().value).toEqual(30);
});
});
在测试运行中,state().value
为15,预期值为30。这意味着它记录了第一个更改,但没有记录第二个更改。
答案 0 :(得分:1)
在Node中执行IO绑定代码时,这些操作的回调不在当前上下文中运行,而是将它们放回到事件循环(more info)中,然后在下一个“滴答”中执行。这实际上意味着,当您运行调用异步代码的同步代码时,即使该异步代码实际上不是 异步(例如Promise.resolve
),它仍将在之后运行同步代码已运行完毕。
在您的情况下,正如您在代码注释中提到的那样,只有在引入Promise.resolve
时测试才开始失败,这是因为在这一点上,我们将状态更改转移到其他上下文,然后测试运行到完成并断言失败(如您所希望的那样,现在知道为什么)。
要修复测试,这非常简单,因为知道状态更新现在将在下一个刻度执行,所以我们只想安排断言来执行相同的操作:
it('should update value properly', done => {
const wrapper = shallow(<Component />, { disableLifecycleMethods: true });
wrapper.instance().componentDidMount();
process.nextTick(() => {
expect(wrapper.state().value).toEqual(30);
done();
})
});
这会将断言与状态更新运行在相同的滴答声上,并且根据文章,我们知道事件循环阶段遵循FIFO模型,因此我们可以保证断言将在状态更新后运行。