试图理解一个奇怪的例子,其中只有一个空洞的行为似乎可以消除警告

Luí*_*eza 5 reactjs react-testing-library

我正在使用 React 测试库来测试组件,并且遇到了一个场景,其中唯一似乎可以消除警告

Warning: An update to MyComponent inside a test was not wrapped in act(...).
Run Code Online (Sandbox Code Playgroud)

是通过使用

await act(async () => {});
Run Code Online (Sandbox Code Playgroud)

现在,我承认我并不完全理解什么act()是,有时我会尝试使用act()和的不同变体waitFor()的不同变体,异步和非异步,直到警告消失,但这次我无法在不使用的情况下做到这一点空回调,感觉不对,我确信将来会失败,所以我想更好地了解发生了什么。

我创建了一个显示该问题的沙箱: https: //codesandbox.io/s/cranky-ben-kfxm7q

一些注意事项:

  • 我认为这里最重要的是我的组件有一个效果,在渲染时加载一些数据,然后它以一种会导致效果再次运行的方式更新状态,因为它依赖于相同的数据,但第二个时间上屏幕上不会发生任何变化。我知道这可能看起来很复杂,但事实是我需要在数据更改时运行效果,这可能会在效果之外发生,例如通过单击按钮。请尽量不要把注意力集中在这一点上
  • 出于某种原因,这<Tooltip />似乎很重要,尽管我们没有在测试中断言它
  • 该按钮并不重要,我只是添加它来显示如何data在效果之外进行更改,从而使效果再次运行

这个例子可能没有多大意义,但请理解,我必须将我的实际组件简化到仍然显示警告的最低限度。理想情况下,我正在寻找一种无需修改组件即可修复测试的方法,因为更改实际组件可能并不那么容易。

Mic*_*iti 2

您收到的错误通知您setState()在尝试渲染挂钩后运行,并且您的测试可能会不稳定。

act()确保等待所有useEffect()状态更新执行。由于重新渲染和状态更新是异步的,因此您需要等待它们。

例子:

  • “我的活动召唤setState()
  • “我的承诺,当它解决时,召唤setState()
  • “我想在 onMount 上调用端点,然后运行setState()”。
  • “触发一个事件,该事件setState()调用重新呈现钩子useEffect(),触发setState()该事件再次调用重新呈现钩子,从而触发useEffect()......等等”

流程是相同的:异步代码 -> setState()

“有时”错误会被消除,因为您的测试是在您不等待的承诺内。

it(async () => {
  // No "await" used inside here
})
Run Code Online (Sandbox Code Playgroud)

当测试async东西时,总是使用组合try/catch(文档文档示例)。这可以确保测试等待承诺履行/拒绝并且已调用该数量的承诺。async/awaitexpect.assertions(N)Nexpect()

如果测试不是 async但您正在运行异步逻辑而不等待,则测试将不会通过。

如果测试正在进行 async但您没有等待,则测试将通过。

我最近遇到了这个,这可能很简单(文档):

// ...
// Wait until the callback does not throw an error. In this case, that means
// it'll wait until the mock function has been called once.
await waitFor(() => expect(mockAPI).toHaveBeenCalledTimes(1))
// ...
Run Code Online (Sandbox Code Playgroud)