React Testing Library: waitFor vs act

Viewed 4068

I have an async method that changes the state by callback:

class Demo{
  callback: () => void;
    
  async triggerCallback() {
    this.callback();
  }

}

This callback is being used to hide/show an element:

const DemoComponent: React.FunctionComponent<Props> = ({Demo}) =>{
  const [show, setShow] = useState(false);

  const changeStateCallback = () => {
    setState(true);
  }

  useEffect(() =>{
   Demo.callback = changeStateCallback;
  }, []);

  
  return(
    <>
      {show && <p>Foo</p>}
    </>
  )
};

I now want to test this and I'm still learning testing with Jest and React Testing Library, so at first I had:

it("test", async () => {
  const Demo = new Demo();

  render(<DemoComponent Demo={Demo}/>);

  await Demo.triggerCallback();

  expect(screen.queryByText("Foo")).toBeTruthy();
}

And this failed because it complained that Demo.triggerCallback(); had to be wrapped in an act():

When testing, code that causes React state updates should be wrapped into act(...):

act(() => { /* fire events that update state / }); / assert on the output */

Which I did with the async/await rules:

it("test", async () => {
  const Demo = new Demo();

  render(<DemoComponent Demo={Demo}/>);

  await act( async () =>{
      await Demo.triggerCallback();
  };

  expect(screen.queryByText("Foo")).toBeTruthy();
}

It failed again saying I couldn't make act async:

Warning: The callback passed to ReactTestUtils.act(...) function must not return anything.

It looks like you wrote ReactTestUtils.act(async () => ...), or returned a Promise from the callback passed to it. Putting asynchronous logic inside ReactTestUtils.act(...) is not supported.

After some research in the docs and google, I found this waitFor() method but together with expect, e.g.:

await waitFor(() => {
    expect(getByText('1')).toBeInTheDocument();
  });

But for sake of it, I replaced act() for waitFor() since it's one of the few async methods I found in the react testing library. Final look (I wrapped it in a try/catch just in case):

it("test", async () => {
  const Demo = new Demo();

  render(<DemoComponent Demo={Demo}/>);
  
  try {
    await waitFor( async () =>{
        await Demo.triggerCallback();
    };
  } catch(e) {
    fail("Test failed": + e);
  }

  expect(screen.queryByText("Foo")).toBeTruthy();
}

And the test passes! Now... is this correct? Just because it works I'm unsure of the solution so would like some feedback on this. I feel I'm doing something illegal because I could only find waitFor() examples with expect() and nothing else.

0 Answers
Related