testing-library/react-hooks-testing-library

Asyncs utils: You called act(async () => ...) without await

enzzoperez opened this issue ยท 6 comments

Hi!, Im starting to test my hooks (in a POC stage), I have a simple test that works but I get the following warning that I dont know how can it solved ๐Ÿค”

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 () => ...);

      at console.error (node_modules/@testing-library/react-hooks/lib/core/console.js:19:7)
      at printWarning (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:68:30)
      at error (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:44:5)
      at node_modules/react-test-renderer/cjs/react-test-renderer.development.js:15297:13
      at tryCallOne (node_modules/promise/lib/core.js:37:12)
      at node_modules/promise/lib/core.js:123:15
      at flush (node_modules/asap/raw.js:50:29)

this is my hook

import {useState, useEffect} from 'react';

const defaultValue = {data: 'Default'};

const useRemoteData = ({data}: any) => {
  const [response, setResponse] = useState<any>();
  const [error] = useState<any>();
  const [isLoading] = useState(true);

  useEffect(() => {
    data ? setTimeout(() => setResponse(data), 500) : setResponse(defaultValue);
  }, [data]);

  return {response, error, isLoading};
};

export default useRemoteData;

and in my test file, Im trying to test that if the props change, the data will change too

  import {renderHook} from '@testing-library/react-hooks';
  import useRemoteData from './useRemoteData';


  it('Init error false ', async () => {
    const {result, waitFor, rerender} = renderHook(
      ({data}) => useRemoteData({data}),
      {
        initialProps: {data: 'first render'},
      },
    );

    await waitFor(() => {
      expect(result.current.response).toEqual('first render');
    });

    rerender({data: 'second render'});

    await waitFor(() => {
      expect(result.current.response).toEqual('second render');
    });
  });

Am I forgetting something in the test file?

Thanks!

tcank commented

Same issue here! The problem comes when using more than one awaited waitFor in the same it
Already read the act behavior in this PR https://github.com/callstack/react-native-testing-library/pull/969/files and tried all the suggestions of this issue callstack/react-native-testing-library#379.

tcank commented

We can fix the problem using the solution proposed on https://stackoverflow.com/questions/64952449/react-native-testing-act-without-await/69201830#69201830
It seems that react-native jest preset is replacing global Promise implementation (2020 issue facebook/react-native#29303) and this leads to the act-await warning.

The patch consists in add one previous preset and one post preset in order to save the original implementation and restore it after react-native preset is set. We do this and found that many tests started to fail (many), and in order to not debug each one, we change the patch to save the original implementation (in first preset) in global.nativePromise and polyfilled in global.polyfilledPromise (in last preset). Later, when we need to fix the Act Await warning, we swap the implementations only for a specific test suite calling fixActAwaitWarning:

const fixActAwaitWarning = (): void => {
  beforeAll(() => {
    global.Promise = global.nativePromise;
  });
  afterAll(() => {
    global.Promise = global.polyfilledPromise;
  });
};

Thank you for your effort and investigation into this @tcank.

We can fix the problem using the solution proposed on https://stackoverflow.com/questions/64952449/react-native-testing-act-without-await/69201830#69201830 It seems that react-native jest preset is replacing global Promise implementation (2020 issue facebook/react-native#29303) and this leads to the act-await warning.

The patch consists in add one previous preset and one post preset in order to save the original implementation and restore it after react-native preset is set. We do this and found that many tests started to fail (many), and in order to not debug each one, we change the patch to save the original implementation (in first preset) in global.nativePromise and polyfilled in global.polyfilledPromise (in last preset). Later, when we need to fix the Act Await warning, we swap the implementations only for a specific test suite calling fixActAwaitWarning:

const fixActAwaitWarning = (): void => {
  beforeAll(() => {
    global.Promise = global.nativePromise;
  });
  afterAll(() => {
    global.Promise = global.polyfilledPromise;
  });
};

Do you mean to change the content of restore-promise.js like this:

global.polyfilledPromise = Promise;

I found it really works!

This is still happening. It only happens when I use more than one awaited waitFor in the same it.

faiwer commented

I came to this solution:

// https://github.com/testing-library/react-hooks-testing-library/issues/825
const asyncAct = (fn: () => Promise<unknown>) =>
  new Promise((resolve) => {
    act(fn).then(resolve);
  });

Using it instead of act avoid the warning.