Reputation: 9633
I have a simple component where you click a button and it uses fetch to call an API and log the result:
class App extends Component {
constructor(props) {
super(props);
this.state = {isLoading: false};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({isLoading: true});
return fetch('https://api.github.com/users/londonappdev')
.then(res => {
return res.json();
})
.then(data => {
console.log(data);
this.setState({isLoading: false})
});
}
render() {
return (
<div className="App">
<button onClick={this.handleClick}>Click</button>
</div>
);
}
}
I am trying to test that isLoading
is set to true
while the request is in progress and then is set to false
once the request is complete.
My test looks like this:
it('sets isloading to true while loading', () => {
global.fetch = jest.fn().mockReturnValue(Promise.resolve({
ok: true,
json: () => Promise.resolve({'user': 'test'})
}));
const c = shallow(<App />);
c.instance().handleClick().then(() => {
expect(c.instance().state.isLoading).toEqual(false);
});
expect(c.instance().state.isLoading).toEqual(true);
});
This works, however my question is: Is it safe/reliable to test state this way? Since setState
is asynchronous, is it possible that expect(c.instance().state.isLoading).toEqual(true);
would be called before the state is properly set?
Any advice on the best way to test this type of component would be much appreciated.
Upvotes: 4
Views: 2776
Reputation: 1542
Yes that's perfectly safe. Similarly to setTimeout(callback, 0)
, promise callbacks are queued to run after the rest of the synchronous code. This ensures your assertions will run at the correct points in your program.
Small tip for unit testing - instead of calling c.instance().handleClick()
I would call c.find('button').prop('onClick')()
. Your component is the "unit" that you're testing and so you should try to avoid accessing internal methods / properties
Upvotes: 5