reactjs - @testing-library/react-hooks 调用 setTimeout 两次

标签 reactjs typescript react-hooks react-testing-library react-hooks-testing-library

这是我的自定义 React-hook。

import { useEffect, useRef } from 'react'

function useInterval({ callback, interval, delay }) {
  const savedTimerId = useRef<NodeJS.Timeout>()

  useEffect(() => {
    const loop = () => {
      const res = callback()
      const nextIteration = () => {
        savedTimerId.current = setTimeout(loop, interval)
      }
      if (res instanceof Promise) {
        res.then(nextIteration)
      } else {
        nextIteration()
      }
    }
    let delayedTimerId: NodeJS.Timeout
    if (!delay) {
      loop()
    } else {
      delayedTimerId = setTimeout(loop, delay)
    }
    return () => {
      // @ts-ignore
      clearTimeout(savedTimerId.current)
      if (delayedTimerId) {
        clearTimeout(delayedTimerId)
      }
    }
  }, [callback, interval, delay])
}

export { useInterval }

这是单元测试

import { renderHook } from '@testing-library/react-hooks'
import { useInterval } from '../useInterval'

describe("Test scenarios for 'useInterval' hook", () => {
  jest.useFakeTimers()

  it("Should call 'callback' once", () => {
    const callback = jest.fn()
    const interval = 10000
    const params = { callback, interval }
    renderHook(() => useInterval(params))
    expect(setTimeout).toHaveBeenCalledTimes(1)
    expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), interval)
  })
})

但这就是输出

Error: expect(jest.fn()).toHaveBeenCalledTimes(expected)

Expected number of calls: 1
Received number of calls: 2

我调试了这个片段。我发现在 useInterval 调用之前,某些东西已经触发了 setTimeoutdebug result

似乎已在内部调用了setTimeout。我做错了什么?有什么想法吗?

最佳答案

你说得对,@testing-library/react-hooks正在后台调用setTimeout,你可以通过以下方式确认这一点:

jest.useFakeTimers()
renderHook(() => {})
expect(setTimeout).toHaveBeenCalledTimes(1)

您可能最好关注调用 callback 的次数,而不是 setTimeout:

afterEach(() => {
  jest.clearAllMocks();
  jest.useRealTimers();
});

describe("Test scenarios for 'useInterval' hook", () => {
  it("Should call 'callback' immediately", () => {
    jest.useFakeTimers()

    const callback = jest.fn()
    const interval = 10000
    const params = { callback, interval }
    renderHook(() => useInterval(params))

    expect(callback).toHaveBeenCalledTimes(1)
  })

  it("Should call 'callback' repeatedly", () => {
    jest.useFakeTimers()

    const callback = jest.fn()
    const interval = 10000
    const params = { callback, interval }
    renderHook(() => useInterval(params))

    jest.advanceTimersByTime(interval * 2)

    // Expect 3 calls: 1 immediate call and 2 interval calls:
    expect(callback).toHaveBeenCalledTimes(3)
  })
})

关于reactjs - @testing-library/react-hooks 调用 setTimeout 两次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63369917/

相关文章:

javascript - 如何在 react 组件中打印 Prop 数据?

javascript - 如何在不包含 React 的情况下发布 React 组件

typescript - 如何在 Angular 7 的 ngx-dropzone-wrapper 中显示服务器上的现有文件

TypeScript 分布式条件类型 - 附加类型参数约束

module - 为现有模块创建一个 d.ts 文件

javascript - 如何根据平均评分填写评论图标?

javascript - Bootstrap 导航栏折叠切换按钮未单击

javascript - useEffect Hook 依赖项未更新

reactjs - 如何在类组件中使用 react-redux useSelector?

javascript - clearInterval 没有在 useEffect 中停止我的计数器