I'm having issues testing asynchronous events with the shallow renderer. Here's an example:
const Actions = {
someAsync() {
return Promise.resolve();
}
};
const Test = React.createClass({
getInitialState() {
return {counter: 0};
},
render() {
console.log('rendering!', this.state.counter);
return (
<div>
<div id='counter'>{this.state.counter}</div>
<button onClick={this._handleClick}>
Increment
</button>
</div>
);
},
_handleClick() {
Actions.someAsync()
.then(() => this.setState({counter: this.state.counter + 1}));
}
});
describe('<Test />', function() {
it('increments when clicked', function() {
const wrapper = shallow(<Test />);
const promise = Promise.resolve();
sinon.stub(Actions, 'someAsync', () => promise);
wrapper.find('button').simulate('click');
return promise
.then(() => {
console.log('testing!');
expect(wrapper.find('#counter').text()).to.equal('1');
});
});
});
The console output is:
LOG: 'rendering!', 0
LOG: 'rendering!', 1
LOG: 'testing!'
So, I'm pretty sure the test is running after the re-render. When I switch to using mount, the tests pass.
Any ideas?
@rylanc
ShallowRendering requires updating a shallowRender tree manually.
simulate method updates the shallowRender tree after calling event handler.
In this case, you need to call update manually because you are calling setState asynchronously.
expect(wrapper.update().find('#counter').text()).to.equal('1');
This appears to be answered - happy to reopen if not.
@koba04's link has changed since this was posted, here's a permalink for others using this issue as a reference.
Thanks!! I'll just update the original link in-place.
wow guys... I just want to share my experience. No matter what I did, I was not able to get the component to update.
Turns out that if you want to test a catch like:
Actions.someAsync()
.then(() => this.setState({counter: this.state.counter + 1}))
.catch(() => this.setState({counter: -1}))
In your test you have to...
return promise
.then(() => {}) <- if you omit this, the next catch() will be fired before the one in your component
.catch(() => {
expect(wrapper.update().find('#counter').text()).to.equal('-1');
});
I know this is more of a Promise thing, you can verify it in your browser as such:
var p = Promise.reject('ads');
p.then(function() { console.log('then1'); }).catch(function() { console.log('catch1'); });
p.catch(function() { console.log('catch2'); });
p.then(function() { console.log('then3'); }).catch(function() { console.log('catch3'); });
// prints:
// catch2
// catch1
// catch3
@mikegleasonjr I ran into this exact same issue an hour ago and your very timely post saved me a lot of time trying to figure out what was going on! <3
@will3216 Thank you very much for the feedback, I lost like 2 hours trying to figure this thing out. I'm glad to know it helped you! Next time someone help me like that I'll tell them!
@ljharb regarding @mikegleasonjr 's last comments: I'm confused, this accepted behaviour? The workaround posted is kinda... well, a workaround.
Yes, this is the nature of asynchrony in JS, combined with the fact that React doesn鈥檛 offer any way to know when async actions have completed.
myComponent.setState({}, () => {
// state is guaranteed up-to-date
});
Just don't test via simulate('click') or whatever since React is responsible for passing click events, you just care that your handlers behave correctly
myComponent.instance().onClick({ /* whatever mock or stub you need */ })
function tick(component) {
return new Promise(resolve => component.setState({}, resolve));
}
toggleSwitch.instance().onChange({ target: { checked: true }});
tick(toggleSwitch).then(() => {
expect(toggleSwitch.state().checked).toBe(true);
});
@rylanc
ShallowRendering requires updating a shallowRender tree manually.
simulatemethod updates the shallowRender tree after calling event handler.In this case, you need to call
updatemanually because you are callingsetStateasynchronously.expect(wrapper.update().find('#counter').text()).to.equal('1');
No, this doesn't work for me. Still the same issue.
@rylanc
ShallowRendering requires updating a shallowRender tree manually.
simulatemethod updates the shallowRender tree after calling event handler.In this case, you need to call
updatemanually because you are callingsetStateasynchronously.expect(wrapper.update().find('#counter').text()).to.equal('1');
No, this doesn't work for me. Still the same issue.
Most helpful comment
@rylanc
ShallowRendering requires updating a shallowRender tree manually.
simulatemethod updates the shallowRender tree after calling event handler.https://github.com/airbnb/enzyme/blob/b4007e6cf180af6dcbd8c2c9be11fdaaa07a4b92/src/ShallowWrapper.js#L484
In this case, you need to call
updatemanually because you are callingsetStateasynchronously.