Search code examples
reactjsreact-nativejestjsreact-navigationreact-navigation-v5

Async testing React Navigation 5 in Jest: NavigationContainer causes console error


I'm using react-native-testing-library and after upgrading react-navigation from 4 to 5, I followed these instructions: https://callstack.github.io/react-native-testing-library/docs/react-navigation to upgrade most of my test suite.

So far so good. The crux here is basically to wrap your tests in a NavigationContainer so my components have access to those hooks that previously came from react-navigation-hooks.

This works fine when my tests are synchronous, but as soon as I add the async keyword to the test function, I get the following warning:

console.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 */

    This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/docs/test-utils.html#act
        in ForwardRef(NavigationContainer)

There are plenty of tests that I run synchronously and which succeed. But in some components, I do run some async logic to get the desired result.

From what I understand, I should wrap any async code in an act call. However, even in this case, I can't get rid of this error.


I went ahead to try and narrow down the issue. Now, all I do is render the wrapped component like this:

test('a simple test', async () => {
  const component = (
    <NavigationContainer>
      <AppNavigator />
    </NavigationContainer>
  );

  const {queryByText} = render(component);
  expect(queryByText('test')).toBeNull();
});

And I'm still running into the same issue. Be aware that the actual test succeeds, only that I'm still getting this console error.

Next, I tried to wrap the render call in act and waitForElement, because that's what I'm supposed to do when running async logic. But it seems that the async code here is executed after the rendering happens.

So I went on to investigate which part of NavigationContainer is responsible for firing the async logic responsible for the error, and it seems that this bit of code (line 49-51) does something interesting:

const [isReady, initialState = rest.initialState] = useThenable(
  getInitialState
);

getInitialState is the result of destructuring the return value of the useLinking call some lines before (43-47). Without going into further detail, getInitialState is wrapped inside a promise in there, so it becomes "thenable".

Now, if I uncomment the useThenable, the console error goes away. But obviously that's not something I can easily achieve from outside this file. So I'm a bit stuck here because I don't know how to write my test code in an async way without running into this error all the time, and I don't feel like ignoring or suppressing it is a good idea either.

Any help would be appreciated.


Solution

  • My recommendation after calling render from react-native-testing-library for components that do async work, useEffect (acting as componentDidMount), changing state, etc:

    await act(async () => {})
    

    Or even:

    await act(async () => await flushMicrotasksQueue())
    

    If you have some timeouts around.