Skip to content

Error thrown - Warning: You called act(async () => ...) without await. #379

Closed as not planned

Description

@jpmonette

Ask your Question

I have a simple test that seems to generate the right snapshot at the end of execution, but throws me a console.error during the execution.

Here are the steps to get to the expected component state:

  1. Load the component
  2. Wait for the useEffect asynchronous logic to be executed so that the SegmentedControlIOS is exposed (testID = recordTypePicker)
  3. Set the selectedSegmentIndex to "1"
  4. Wait for the component to re-render and all the async logic to be executed
  5. Assert that the rendered component includes newSObjectLayout testID

The test

import * as React from 'react';
import { fireEvent, render, waitFor } from 'react-native-testing-library';
import { NewSObjectContainer } from '../NewSObject';

describe('NewSObjectContainer', () => {
  const setup = () => {
    const route = { params: { sobject: 'Account' } };
    const navigation = { setOptions: jest.fn() };

    const container = render(<NewSObjectContainer route={route} navigation={navigation} />);
    return { container };
  };

  it('should render a NewSObjectContainer - with page layout exposed', async () => {
    const { container } = setup();

    await waitFor(() => expect(container.getByTestId('recordTypePicker')).toBeTruthy());

    const input = container.getByTestId('recordTypePicker');
    fireEvent(input, 'onChange', { nativeEvent: { selectedSegmentIndex: 1 } });

    await waitFor(() => expect(container.getByTestId('newSObjectLayout')).toBeTruthy());

    expect(container.toJSON()).toMatchSnapshot();
  });
});

The console log

./node_modules/.bin/jest src/containers/__tests__/NewSObject.spec.tsx --u
  console.error
    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 CustomConsole.console.error (node_modules/react-native/Libraries/YellowBox/YellowBox.js:63:9)
      at printWarning (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:120:30)
      at error (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:92:5)
      at node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14953: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)

 PASS  src/containers/__tests__/NewSObject.spec.tsx
  NewSObjectContainer
    ✓ should render a NewSObjectContainer - with page layout exposed (499ms)

 › 1 snapshot written.
Snapshot Summary
 › 1 snapshot written from 1 test suite.

Test Suites: 1 passed, 1 total
Tests:       q passed, q total
Snapshots:   1 written, 1 passed, q total
Time:        4.865s, estimated 8s
Ran all test suites matching /src\/containers\/__tests__\/NewSObject.spec.tsx/i.

Activity

  1. changed the title [-]Error thrown - 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 () => ...);[/-] [+]Error thrown - Warning: You called act(async () => ...) without await.[/+] on Jun 9, 2020
  2. mdjastrzebski commented on Jun 9, 2020

    @mdjastrzebski
    Member

    This particular line looks a bit odd to me:

    await waitFor(() => expect(container.getByTestId('recordTypePicker')).toBeTruthy())

    I don't thing we support putting jest's expect into waitFor.

    Could you try something like:

    const element = await findByTestId('record...') // This is essentially waitFor & getByTestId
    expect(element).toBeTruthy()

    Let me know if you still got the same error.

  3. jpmonette commented on Jun 9, 2020

    @jpmonette
    Author

    @mdjastrzebski
    I did update my code and still get the issue. Here's the updated test:

    import * as React from 'react';
    import { fireEvent, render } from 'react-native-testing-library';
    import { NewSObjectContainer } from '../NewSObject';
    
    describe('NewSObjectContainer', () => {
      const setup = () => {
        const route = { params: { sobject: 'Account' } };
        const navigation = { setOptions: jest.fn() };
    
        const container = render(<NewSObjectContainer route={route} navigation={navigation} />);
        return { container };
      };
    
      it('should render a NewSObjectContainer - with Record Type picker exposed', async () => {
        const { container } = setup();
    
        const input = await container.findByTestId('recordTypePicker');
        expect(input).toBeTruthy();
    
        expect(container.toJSON()).toMatchSnapshot();
      });
    
      it('should render a NewSObjectContainer - with page layout exposed', async () => {
        const { container } = setup();
    
        const input = await container.findByTestId('recordTypePicker');
        expect(input).toBeTruthy();
    
        fireEvent(input, 'onChange', { nativeEvent: { selectedSegmentIndex: 1 } });
    
        const layout = await container.findByTestId('newSObjectLayout');
        expect(layout).toBeTruthy();
    
        expect(container.toJSON()).toMatchSnapshot();
      });
    });
  4. mdjastrzebski commented on Jun 9, 2020

    @mdjastrzebski
    Member

    @jpmonette can you provide a repro repo?

  5. jsamr commented on Aug 3, 2020

    @jsamr

    @mdjastrzebski I ran into a similar issue (although, don't know if it is exactly the same problem) and made a mcve here: https://github.com/jsamr/react-native-testing-library-379

  6. pillu00 commented on Aug 3, 2020

    @pillu00

    I also ran into same problem after upgrading to v7.0.1.
    Also, if you just run the react-navigation example in the repo itself you will see this problem.

  7. mdjastrzebski commented on Aug 3, 2020

    @mdjastrzebski
    Member

    @jsamr thanks for posting repo, I'll try to reproduce that tomorrow.

  8. mdjastrzebski commented on Aug 4, 2020

    @mdjastrzebski
    Member

    @jsamr You should wrap the reload() call in act like this:

        act(() => {
          result.instance.reload();
        });

    As each state mutation should be wrapped in act. In case of fireEvent this is done for you.

    This leaves your code with following warning logged to console

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

    which is simingly caused by
    await findByText('load-cycle-1');

  9. pillu00 commented on Aug 4, 2020

    @pillu00

    @mdjastrzebski ,
    I ran reactnavigation example in this repo and still getting the above error. But when i downgrade to v6.0.0, it goes away. Can you please help me what's going wrong ?

  10. DracotMolver commented on Aug 6, 2020

    @DracotMolver

    I'm facing the same error but my test is different. Before moving from version 2 to 7 my test was the same and I have no errors, but after updating to v7 i've been getting the same error.

    I have something really simple like this:

     const { getByText, update } = render(<Component />);
    
     fireEvent(getByText('idx-1'), 'onPress');
    
     await act(async () => {
        update(<Component />);
     });

    I use act because when I fire the event I have some async logic in my component that will re-render it. If i don't use the last code I've got the next error:

     Warning: An update to ForwardRef(NavigationContainer) inside a test was not wrapped in act(...).
          
          When testing, code that causes React state updates should be wrapped into act(...):
          
          act(() => {
            /* fire events that update state */
          });
          /* assert on the output */

    UPDATE: 11-08-2020
    I changed (cleaned) my tests. I don't have to use anymore this awful thing I was doing adding await to all the act functions I was using and It worked fine. BUT, this is not always for all the cases, so you must be careful how is written your async code and then how is written your test... testing this async code. Also, you must be careful if you are using async callings inside the useEffect hook. I was concerned about this but I was doing it wrong in some parts. I'll share with you what I'm doing with my code now, which thanks to the tests and the library, I think is more solid:

    I'm adding a variable to know if you component is mounted/unmounted. With this variable validate wether update or not the state of the componente, which cause to re-render the component.

    // I prefer to use isMounted. This way I don't have to use a negation to validate if something is  `true` when is `false` :/
    useEffect(() => {
      let isMounted = true;
    
     (async ( ) => {
       const result = await someAsyncMethod();
    
      // Here i want to update the state of my component
      if (isMounted) {
       setState(result);
      }
     })();
    
      return () => {
        isMounted = false;
      };
    })

    My test now it is like this:

    it('test', () => {
     const { getByText, update } = render(<Component />);
    
     fireEvent(getByText('idx-1'), 'onPress');
    
      act(() => {
        update(<Component />);
      });
    });

    Hope this might help :)

    One last thing! Don't forget to use the done function 😋

  11. renanmav commented on Aug 12, 2020

    @renanmav

    This leaves your code with following warning logged to console

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

    I’ve noticed that this error/warning happens when I use more than 1 await within a it statement

  12. dnlsilva commented on Aug 16, 2020

    @dnlsilva

    This leaves your code with following warning logged to console

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

    I’ve noticed that this error/warning happens when I use more than 1 await within a it statement

    Same issue here.
    I tried all the things I mentioned earlier, but none worked here.

  13. florian-milky commented on Aug 18, 2020

    @florian-milky

    Same issue and I'm not doing anything in my test, just rendering and await findBy

  14. 97 remaining items

  15. mmomtchev commented on Aug 22, 2022

    @mmomtchev

    I think that the only solution which addresses the root cause is to remove asap from React Native. If there is a good reason for having it there, then the next best one is to make sure RNTL skips it in some way. Everything else is covering it up.

  16. mdjastrzebski commented on Aug 22, 2022

    @mdjastrzebski
    Member

    @Norfeldt @milesj @mmomtchev do you use RNTL Jest preset: preset: '@testing-library/react-native',? Does using it make any difference regarding test runs?

  17. milesj commented on Aug 22, 2022

    @milesj

    Never tried. I don't work on the RN stuff anymore.

  18. Norfeldt commented on Aug 25, 2022

    @Norfeldt

    I'm trying to upgrade to jest 28 as part of my expo upgrade to SDK 46, but it's giving me some trouble.

    software-mansion/react-native-reanimated#3215

    Will report back if anything has change regarding this - once I resolve the blockers.

  19. mdjastrzebski commented on Aug 25, 2022

    @mdjastrzebski
    Member
  20. Norfeldt commented on Aug 25, 2022

    @Norfeldt

    @Norfeldt that's weird our recent basic example uses Expo 46 & Jest 28: https://github.com/callstack/react-native-testing-library/blob/main/examples/basic/package.json

    This basic example does not have reanimated.

  21. Norfeldt commented on Sep 20, 2022

    @Norfeldt

    @mdjastrzebski I'm now on jest 29 and expo SDK 46 and the issue is still there. I believe that it's because I want to render components that uses react-query and fetches data from a local or staging server. I have a way to (almost) know when it's done, but checking that the queryClient has saved/cached the data.

    Is there some way to render with an act - so I can tell RNTL that state updates should be done?

    Currently I'm using .waitFor and not act

    export function renderWithProviders(ui: React.ReactElement) {
      const testQueryClient = createTestReactQueryClient()
    
      const AppProviders = ({ ui }: { ui: React.ReactElement }) => (
        <QueryClientProvider client={testQueryClient}>
          <CurrentPortfolioContextProvider>{ui}</CurrentPortfolioContextProvider>
        </QueryClientProvider>
      )
    
      const { rerender, ...result } = ReactNativeTestingLibrary.render(<AppProviders ui={ui} />)
      return {
        utils: {
          ...result,
          testQueryClient,
          rerender: (rerenderUi: React.ReactElement) => rerender(<AppProviders ui={rerenderUi} />),
        },
      }
    }
    
    export async function renderWithUserAsync(
      Element: JSX.Element,
      email: keyof typeof userCredentials,
      awaitQueryClient?: boolean
    ) {
      await api.signOut() // allows avoiding to clean-up session after each test
      const user = await getSessionAsync(email)
      const { utils } = renderWithProviders(Element)
    
      if (awaitQueryClient === undefined || awaitQueryClient) {
        expect(utils.testQueryClient.getQueryData(QUERY_KEY_USER)).toBeUndefined()
        await waitFor(() => {
          expect(utils.testQueryClient.getQueryData(QUERY_KEY_USER)).toEqual(user)
        })
      }
    
      return { utils, user }
    }
  22. mdjastrzebski commented on Sep 26, 2022

    @mdjastrzebski
    Member

    @Norfeldt @milesj @mmomtchev we've released RNTL v11.2.0 that might have improved this issue. Could you verify if it improves things for you. Pls report your React, React Test Renderer & React Native version along with feedback info so we can get a better understanding of the issue.

  23. Norfeldt commented on Sep 26, 2022

    @Norfeldt
        "expo": "^46.0.10",
         ...
        "@testing-library/jest-native": "^4.0.12",
        "@testing-library/react-native": "^11.2.0",
        ...    
        "jest": "^29.0.3",
        "jest-expo": "^46.0.1",
        "msw": "^0.44.2",
        "patch-package": "^6.4.7",
        "prettier": "^2.5.1",
        "react-test-renderer": "^18.2.0",
        ...
        "ts-jest": "^29.0.1",
        "ts-node": "^10.9.1",
    
    console.error
          Warning: An update to _default inside a test was not wrapped in act(...).
          
          When testing, code that causes React state updates should be wrapped into act(...):
          
          act(() => {
            /* fire events that update state */
          });
          /* assert on the output */
          
          This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
              at _default
              at CurrentPortfolioContextProvider
              at QueryClientProvider
              at AppProviders
    

    As mentioned, I think it's because I do some fetching and react-query does some react state management. If I just had a way to render it and tell it when RQ is done with the state management, then that might solve it.

  24. stevehanson commented on Sep 26, 2022

    @stevehanson
    Contributor

    we've released RNTL v11.2.0 that might have improved this issue. Could you verify if it improves things for you.

    This did fix this error for me. I am in a new codebase that I set up last week. I was on React Native Testing Library 11.1.0, and upgrading to 11.2.0 made it so the warning I was receiving went away. Here was the test where I had been getting the warning, though the test itself was passing. I presume I was getting the warning because I had multiple awaits in it:

    const mocks = [signInMutationMock(...)]
    renderApplication({ mocks })
    
    expect(await screen.findByTestId('SignInScreen')).toBeDefined()
    
    fireEvent.changeText(
      screen.getByLabelText('Email address'),
      'jan@example.com',
    )
    fireEvent.changeText(screen.getByLabelText('Password'), 'Password')
    fireEvent.press(screen.getByRole('button'))
    
    // this was the line that was triggering the console error
    expect(await screen.findByTestId('HomeScreen')).toBeDefined()
    
    fireEvent.press(screen.getByText('Sign Out'))
    await pressAlertButton('Yes, sign out')
    
    expect(screen.queryByTestId('HomeScreen')).toBeNull()
    expect(screen.getByTestId('SignInScreen')).toBeDefined()

    React: 18.1.0
    React Native: 0.70.1
    RN Testing Library: 11.2.0
    React Test Renderer: 18.1.0 (I don't have this added explicitly in my project, included by RN Testing Library)

  25. mdjastrzebski commented on Sep 30, 2022

    @mdjastrzebski
    Member

    Closing this issue, as it became non-actionable. Recent changes seem to have fixed the warnings for some users, while this issue contains various instances of act warnings, both sync and async, triggered by different conditions that probably deserve separate treatment.

    If you will find act warnings still occurring in the latest RNTL version, then please submit a GH issue with minimal repro repository based on ours examples/basic so that we have an actionable PR with clear repro case to fix.

    @jpmonette, @Norfeldt, @mmomtchev, @tcank, @rijk ^

  26. damdafayton commented on Dec 2, 2022

    @damdafayton

    i am having this problem with @testing-library/react-native": "^11.5.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions