reactjs React测试库的renderHook的WaitForNextUpdate超时

9rnv2umw  于 2023-01-02  发布在  React
关注(0)|答案(2)|浏览(133)

我正在用react-testing-library测试一个自定义钩子,它基本上是这样做的:

function useHook() {
  const [state, setState] = useState();
  
  const fetch = async () => {
    const response = await httpCall();
    if (instanceof response !== Error) {
      setState("GOOD")
    } else {
      setState("BAD")
    }
  }
  
  return { state, fetch }
}

我的测试文件是这样的:

it("test", async () => {
  const { result, waitForNextUpdate } = renderHooks(() => useHook())

  await result.current.fetch();
  expect(result.current.state).toBe(undefined)

  await waitForNextUpdate();
  expect(result.current.state).toBe("GOOD") //or at least "BAD"
})

我写这个是因为我调用了异步函数fetch(),它应该触发setState,我Assert还没有发生重新呈现,然后我waitForNextUpdate()以便等待这个重新呈现,我Assert钩子返回的state现在有一个值"GOOD""BAD"
我的问题是我的测试给了我一个错误:Timeout - Async callback was not invoked within the 5000 ms ...,并且在测试等待waitForNextUpdate()时发生此错误。
我不知道我的测试出了什么问题。我确信(因为我测试过了)钩子工作正常,http调用已经发出。我知道在测试中检查值,但也因为钩子在应用程序中工作正常。我不明白为什么状态的更新似乎从未发生。
我是我的团队中第一个用这个工具测试的人,所以我很困惑。

6rvt4ljy

6rvt4ljy1#

首先,在钩子的if语句中有一个小错误,所以让我们纠正它,并添加httpCall函数的导入,例如sake

import { useState } from 'react'
import { httpCall } from './httpCall'

export function useHook() {
  const [state, setState] = useState<string>()

  const fetch = async () => {
    const response = await httpCall()
    if (response instanceof Error) {
      setState('GOOD')
    } else {
      setState('BAD')
    }
  }

  return { state, fetch }
}

现在我们可以有两个测试用例,基于模拟httpCall结果。2注意使用了rerender而不是waitForNextUpdate

import { waitFor } from '@testing-library/react'
import { renderHook } from '@testing-library/react-hooks'
import { httpCall } from './httpCall'
import { useHook } from './useHook'

jest.mock('./httpCall', () => ({
  httpCall: jest.fn()
}))

describe('useHook', () => {
  it('should return error', async () => {
    // httpCall was mocked above, so we can replace it's implementation
    httpCall.mockImplementation(() => new Error('error'))
    const { result, rerender } = renderHook(() => useHook())

    await result.current.fetch()
    rerender()

    await waitFor(() => {
      expect(result.current.state).toBe('BAD')
    })
  })

  it('should return response', async () => {
    httpCall.mockImplementation(() => ({
      ok: true
    }))

    const { result, rerender } = renderHook(() => useHook())

    await result.current.fetch()
    rerender()

    await waitFor(() => {
      expect(result.current.state).toBe('GOOD')
    })
  })
})
eeq64g8w

eeq64g8w2#

当调用钩子的被测函数时,不应该使用await
它应该被 Package 在act()中,尽管在关于异步测试的文档中指定了在调用await waitForNextUpdate()时可以省略它。
此变更后测试通过:

it('test', async () => {
  const { result, waitForNextUpdate } = renderHook(() => useHook());

  act(() => {
    result.current.fetch();
  });
  expect(result.current.state).toBe(undefined);

  await waitForNextUpdate();
  expect(result.current.state).toBe('GOOD'); //or at least "BAD"
});

相关问题