简体   繁体   中英

Testing async `componentDidMount()` with Jest + react-testing-library

I have a component that fetches data asynchronously in componentDidMount()

componentDidMount() {
  const self = this;

  const url = "/some/path";
  const data = {}
  const config = {
    headers: { "Content-Type": "application/json", "Accept": "application/json" }
  };

  axios.get(url, data, config)
    .then(function(response) {
      // Do success stuff

      self.setState({ .... });
    })
    .catch(function(error) {
      // Do failure stuff

      self.setState({ .... });
    })
  ;
}

My test for the component looks like this -

it("renders the component correctly", async () => {
  // Have API return some random data;
  let data = { some: { random: ["data to be returned"] } };
  axios.get.mockResolvedValue({ data: data });

  const rendered = render(<MyComponent />);
  // Not sure what I should be awaiting here
  await ???

  // Test that certain elements render
  const toggleContainer = rendered.getByTestId("some-test-id");
  expect(toggleContainer).not.toBeNull();
});

Since rendering and loading data is async, my expect() statements go ahead and execute before componentDidMount() and the fake async call finish executing, so the expect() statements always fail.

I guess I could introduce some sort of delay, but that feels wrong and of course increases my runtime of my tests.

This similar question and this gist snippet both show how I can test this with Enzyme. Essentially they rely on async / await to call componentDidMount() manually.

However react-testing-library doesn't seem to allow direct access to the component to call its methods directly (probably by design). So I'm not sure "what" to wait on, or whether that's even the right approach.

Thanks!

It depends on what your component is doing. Imagine your component shows a loading message and then a welcome message. You would wait for the welcome message to appear:

const { getByText, findByText } = render(<MyComponent />)
expect(getByText('Loading...')).toBeInTheDocument()
expect(await findByText('Welcome back!')).toBeInTheDocument()

The best way to think about it is to open the browser to look at your component. When do you know that it is loaded? Try to reproduce that in your test.

You need to wrap render with act to solve warning message causes React state updates should be wrapped into act .

eg:

it("renders the component correctly", async () => {
  // Have API return some random data;
  let data = { some: { random: ["data to be returned"] } };
  axios.get.mockResolvedValue({ data: data });

  const rendered = await act(() => render(<MyComponent />));

  // Test that certain elements render
  const toggleContainer = rendered.getByTestId("some-test-id");
  expect(toggleContainer).not.toBeNull();
});

Also same goes for react-testing-library.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM