LondonAppDev
LondonAppDev

Reputation: 9633

React testing state of component with Jest and Enzyme

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

Answers (1)

Billy Reilly
Billy Reilly

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

Related Questions