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

2

8

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!

Faldstool answered 19/9, 2019 at 3:3 Comment(0)
E
6

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.

Emmett answered 19/9, 2019 at 9:40 Comment(2)
Not sure I understand what you meant, findByText is async, you can see it hereEmmett
oh, I was pretty sure it is not. sorry.Hypochondrium
L
0

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

e.g:

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.

Loaiasis answered 4/1, 2023 at 20:29 Comment(1)
No. render method of React Testing Library already wrapped in act. kentcdodds.com/blog/fix-the-not-wrapped-in-act-warningGundry

© 2022 - 2024 — McMap. All rights reserved.