在我的测试中,我想阻止我的主线程,直到我的一个组件完成其生命周期方法后,通过componentDidUpdate()
,在我触发一个导致它将子组件添加到自身的事件之后。我怎么能这样做?
这样的事情:
describe('my <Component />', () => {
it('should do what I want when its button is clicked twice', () => {
const wrapper = mount(<Component />);
const button = wrapper.find('button');
// This next line has some side effects that cause <Component /> to add
// some children components to itself...
button.simulate('click', mockEvent);
// ... so I want to wait for those children to completely go through
// their lifecycle methods ...
wrapper.instance().askReactToBlockUntilTheComponentIsFinishedUpdating();
// ... so that I can be sure React is in the state I want it to be in
// when I further manipulate the <Component />
button.simulate('click', mockEvent);
expect(whatIWant()).to.be.true;
});
});
(我想这样做是因为,现在,我收到了这个警告:
Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op.
我相信我得到它是因为我的测试导致我的组件更快地改变其内部状态,而不是React的内部多线程魔法可以跟上,所以当我运行{{1}时第二次,React已经实例化了新的子组件,但尚未安装它们。我认为等待React完成更新我的Component及其子代是解决该问题的最佳方法。)
答案 0 :(得分:2)
尝试将expect()
块包裹在setImmediate()
块中:
describe('my <Component />', () => {
it('should do what I want when its button is clicked twice', (done) => {
const wrapper = mount(<Component />);
const button = wrapper.find('button');
button.simulate('click', mockEvent);
button.simulate('click', mockEvent);
setImmediate(() => {
expect(whatIWant()).to.be.true;
done();
});
});
});
这里发生了什么:为了处理异步性,Node和大多数浏览器在幕后都有一个事件队列。每当像Promise或IO事件这样的东西需要异步运行时,JS环境会将它添加到队列的末尾。然后,只要同步代码完成运行,环境就会检查队列,选择队列前面的任何内容,然后运行它。
setImmediate()
在队列后面添加一个函数。一旦当前队列中的所有内容都完成运行,传递给setImmediate()
的函数中的任何内容都将运行。因此,无论React异步执行什么操作,将expect()
包装在setImmediate()
内都会导致测试等到React完成后面的异步工作。
这是一个很棒的问题,其中包含有关setImmediate()
的更多信息:setImmediate vs. nextTick
以下是节点中setImmediate()
的文档:https://nodejs.org/api/timers.html#timers_setimmediate_callback_args