'Jest - Warning: You called act(async () => ...) without await

I have a test where I'm trying to test a component's default render compared to a render when something (in this case a 0) is in storage. The component should render the same in both cases. To test this, I need to render the component twice.

import { render, waitFor } from "@testing-library/react-native";
import * as React from "react";
import { View } from "react-native";

it("Shouldn't break when rendering twice...", async () => {
    const firstRender = await waitFor(() => render(<View />));
    const defaultJson = JSON.stringify(firstRender.toJSON()); 
    firstRender.unmount();
  
    putTheThingInAsyncStorage();
    
    const secondRender = await waitFor(() => render(<View />));
    const newJson = JSON.stringify(secondRender.toJSON()); 
    expect(newJson).toBe(defaultJson);
  
    // In this particular test case, they should be the same.
    expect(newJson).toBe(defaultJson);

    removeTheThingFromAsyncStorage();
});

This test works, but the console gives me this nagging warning: Warning: You called act(async () => ...) without await. This could lead to unexpected testing behaviour, interleaving multiple act calls and mixing their scopes. You should - await act(async () => ...);

Because of this, I've added the await waitFor(...); to the render calls, but I still get the warning. At this point, the warning is lying to me because I am, in fact, using the await keyword so I'm not sure what the issue is.

What am I doing wrong?



Solution 1:[1]

It's still confusing to me that I was getting the specific warnings that I was getting, but here's what seemed to work for me:

import { render, waitFor } from "@testing-library/react-native";
import * as React from "react";
import { View } from "react-native";

it("Shouldn't break when rendering twice...", async () => {
  // DO NOT use await or put render() in a waitFor
  const firstRender = render(<View/>);
  const defaultJson = JSON.stringify(firstRender.toJSON());
  act(() => {
    // Unmount should be wrapped in an act, but don't use await
    firstRender.unmount();
  });

  putTheThingInAsyncStorage();

  // DO NOT use await or put render() in a waitFor
  const secondRender = render(<View/>);
  // I did need to use await and waitFor for this toJSON()
  await waitFor(() => secondRender.toJSON());
  const newJson = JSON.stringify(secondRender.toJSON());

  expect(newJson).toBe(defaultJson);

  removeTheThingFromAsyncStorage();
});

Solution 2:[2]

This question has been debated a lot here: https://github.com/callstack/react-native-testing-library/issues/379

Here is my solution:

Create test/asap.js with

const nodePromise = Promise;

module.exports = (r) => nodePromise.resolve().then(r);

Then add to jest.config.js:

moduleNameMapper: {
  '^asap\/+.*$': '<rootDir>/test/asap.js',
}

This will short-circuit the asap module which is used by the internal Promise shim of react-native and is not needed for the jest environment.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Austin Brown
Solution 2 mmomtchev