Márton Borlay
Márton Borlay

Reputation: 537

React testing with asynchronous setState

I'm new to React testing and I'm having a hard time figuring out the following issue:

I'm trying to simulate an input onChange event. It's a text input that filters the results in a table. InteractiveTable has a controlled input field (ControlledInput) and an instance of Facebook's FixedDataTable.

This is the test:

let filter = ReactTestUtils.findRenderedComponentWithType(component, ControlledInput);
let input = ReactTestUtils.findRenderedDOMComponentWithTag(filter, 'input');
input.value = 'a';
ReactTestUtils.Simulate.change(input);
console.log(component.state);

On input change the component updates its state with the value of the input, but since setState is asynchronous, here the console.log will log out the previous state, and I can't query the structure of the component for testing, because it's not updated yet. What am I missing?

Edit: to be clear, if I make the assertion in a setTimeout, it will pass, so it's definitely a problem with the asynchronous nature of setState.

I found one solution, where I overwrite the componentDidUpdate method of the component:

component.componentDidUpdate = () => {
  console.log(component.state); // shows the updated state
  let cells = ReactTestUtils.scryRenderedComponentsWithType(component, Cell);
  expect(cells.length).toBe(30);
  done();
};

This wouldn't be possible if the component had its own componentDidUpdate method, so it's not a good solution. This seems to be a very common problem, yet I find no solution to it.

Upvotes: 2

Views: 1148

Answers (1)

ericf89
ericf89

Reputation: 379

Normally when I run into similar scenarios when testing, I try to break things apart a little. In your current test, (depending on your flavor of test framework), you could mock the component's setState method, and simply ensure that it's called with what you expect when you simulate a change.

If you want further coverage, in a different test you could call the real setState with some mock data, and then use the callback to make assertions about what's rendered, or ensure other internal methods are called.

OR: If your testing framework allows for simulating async stuff, you could try calling that too and test the whole thing in one go.

Upvotes: 2

Related Questions