React testing-library - 测试在第一个 useEffect 挂钩中设置状态的承诺

React testing-library - Testing a promise that set states in the first useEffect hook

我有一个 useEffect 挂钩,它会在安装组件时加载,如下所示:

useEffect(() => {
   listFiles().then(keys => {
      setKeys(keys)
      console.log(keys)
   }).catch(err => {
        showFail(err.message)
   })
}, [])

我正在尝试使用 react testing-library 测试函数,只需使用渲染函数:

beforeEach(() => {
  render(<Dashboard />)
})

但是,当我 运行 任何解决承诺并设置状态的测试时:

jest.mock('../utils/storage', () => ({
    listFiles: jest.fn(() => Promise.resolve([])),
}))

我最后收到一条关于使用 act 来包装事件的奇怪警告消息:

  Warning: An update to Dashboard 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 .. 
        in Dashboard

      18 |     useEffect(() => {
      19 |         listFiles().then(keys => {
    > 20 |             setKeys(keys)
         |             ^
      21 |             console.log(keys)
      22 |         }).catch(err => {
      23 |             showFail(err.message)

我曾尝试将渲染包裹在 act 中,但它似乎没有任何改变。

关于我在这里做错了什么有什么建议吗?我应该以其他方式渲染吗?

提前致谢!

当您尝试在组件完成更新所有状态之前断言时,通常会发生此错误。

请注意,在 listFiles 中,您正在调用 setKeys(keys) 并更新状态。

您应该 await 新密钥(或文件)显示在文档中:

expect(await findByText('some file name or key')).toBeInTheDocument();
// more asserts here

或者,您可以 waitFor 调用模拟(尽管我认为上面的选项更好)。

await waitFor(() => expect(listFilesMock).toHaveBeenCalled());
// more asserts here

上面的方法应该已经由 React 测试库为您包装在 act 中,所以您不需要这样做


什么是 act() 来自 React docs:

When writing UI tests, tasks like rendering, user events, or data fetching can be considered as “units” of interaction with a user interface. React provides a helper called act() that makes sure all updates related to these “units” have been processed and applied to the DOM before you make any assertions.

The name act comes from the Arrange-Act-Assert pattern.


有用的链接:

在我的例子中,我没有模拟 useEffect 异步函数。

component.test.tsx:

test('Working useEffect hook', () => {
  const dbCallMock = jest.fn(() => Promise.resolve())
  render(<Tracks dbCallMock ={dbCallMock} />)
}

component.tsx:

export const myComponent = (props: {
  dbCallMock: Function
}) => {
  const [state, setState] = useState<string[]>([])

  const getDbState= () => 
    db.state.toArray().then(gotState => setState(gotState))

  useEffect(
    () => (props.dbCallMock ? props.dbCallMock() : getDbState()),
    []
  )
}