在 Jest 中异步测试 React Navigation 5:NavigationContainer 导致控制台错误

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

我正在使用 react-native-testing-library,在将 react-navigation 从 4 升级到 5 之后,我按照以下说明进行操作:https://callstack.github.io/react-native-testing-library/docs/react-navigation 升级了我的大部分测试套件。

到目前为止一切顺利。这里的关键基本上是将测试包装在 NavigationContainer 中,这样我的组件就可以访问以前来自 react-navigation-hooks.

的那些挂钩

当我的测试是同步的时,这工作正常,但是一旦我将 async 关键字添加到测试函数,我就会收到以下警告:

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)

我 运行 同步进行了很多测试并且成功了。但是在某些组件中,我执行 运行 一些异步逻辑以获得所需的结果。

据我所知,我应该将所有异步代码包装在 act 调用中。但是,即使在这种情况下,我也无法摆脱这个错误。


我继续尝试缩小问题范围。现在,我所做的就是像这样渲染包装的组件:

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

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

而且我还在运行陷入同样的​​问题。请注意,实际测试成功,只是我仍然收到此控制台错误。

接下来,我尝试将 render 调用包装在 actwaitForElement 中,因为这是我在 运行 宁异步逻辑时应该做的。但似乎这里的异步代码是在 渲染发生后 执行的。

所以我继续调查 NavigationContainer 的哪一部分负责触发导致错误的异步逻辑,似乎这段代码(第 49-51 行)做了一些有趣的事情:

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

getInitialState 是在 (43-47) 之前的某些行 useLinking 调用的 return 值的解构结果。没有进一步的细节, getInitialState 被包裹在一个承诺里面,所以它变成了 "thenable".

现在,如果我取消注释 useThenable,控制台错误就会消失。但显然,这不是我可以从该文件外部轻松实现的。所以我有点卡在这里,因为我不知道如何以异步方式编写我的测试代码,而不会一直 运行ning 进入这个错误,我不想忽略或抑制它也是个好主意。

如有任何帮助,我们将不胜感激。

我从 react-native-testing-library 调用 render 后对执行异步工作的组件、useEffect(充当 componentDidMount)、更改状态等的建议:

await act(async () => {})

甚至:

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

如果你有一些超时。

Test Renderer 的实例具有卸载 in-memory 树的 unmount() 方法,触发适当的生命周期事件。在测试结束时添加:

component.unmount()

您可以在 https://reactjs.org/docs/test-renderer.html#testrendererunmount 获得更多信息。

我只能通过将 expectrender 调用放在同一个函数中才能让这个按钮消失。从共享函数返回渲染结果导致 ForwardRef 错误。很奇怪,但我把范围缩小到这个。

我还在渲染后调用此 wait() 函数,这让 Apollo 运行 也可以进行 GraphQL 查询:

export async function wait(ms = 0): Promise<void> {
  return await act(async () => {
    return new Promise(resolve => {
      setTimeout(resolve, ms);
    });
  });
}