【问题标题】:Testing a custom UseInterval hook with jest用 jest 测试自定义 UseInterval 钩子
【发布时间】:2022-01-13 12:25:51
【问题描述】:

我的钩子是;

function useInterval() {
  const ref: MutableRefObject<NodeJS.Timer | null > = useRef(null);
  function set(callback: () => void, delay: number) {
    ref.current = setInterval(callback, delay)
  }
  function clear() {
    if (ref.current) {
      clearInterval(ref.current)
      ref.current = null
    }
  }
  return { set, clear }
}

我的测试是;

it("set: This should be called 10 times", () => {
    var callback = jest.fn();
    jest.useFakeTimers()
    const { result } = renderHook(() => hooks.useInterval())
    act(() => {
        result.current.set(() => { callback }, 100)    
        jest.advanceTimersByTime(1000);
    })
    expect(callback).toHaveBeenCalledTimes(10);
    jest.useRealTimers()
})

renderHook()act() 来自"@testing-library/react-hooks": "^7.0.2"

我不断得到的结果是来自我的expect() 电话的0。我似乎无法弄清楚为什么。

如果我只使用setInterval() expect() 得到正确的值

it("setInterval", () => {
    var callback = jest.fn();
    jest.useFakeTimers()
    setInterval(callback, 100)
    jest.advanceTimersByTime(1000);
    expect(callback).toHaveBeenCalledTimes(10);
    jest.useRealTimers()
})

我已经尝试以我能想到的所有可能的逻辑方式重新排列这些行。

我注意到无论有没有act(),我都会得到相同的结果,奇怪的是。

timers: "fake" 或其任何变体(现代/旧版)添加到jest.config.ts 似乎没有任何效果。

显然,testing-library/react-hooks 以某种方式从jest.useFakeTimers() 中掩盖了setInterval(),但我不明白如何实现,因此无法实现我正在寻找的结果。

我的一部分认为我的钩子没有被jest.useFakeTimers() 击中,因为假计时器没有被全局替换,但我不知道该怎么做。

另外,我正在使用 Typescript。并不是说我认为这有什么不同。

【问题讨论】:

  • 那行不通

标签: javascript reactjs react-hooks jestjs react-testing-library


【解决方案1】:

您将匿名函数传递给 set 方法,而不是模拟 callback。所以setInterval 排队的宏任务会调用匿名函数。这就是断言失败的原因。没有什么可以开玩笑的配置,TypeScript。

例如

useInterval.ts:

import { MutableRefObject, useRef } from 'react';

export function useInterval() {
  const ref: MutableRefObject<ReturnType<typeof setInterval> | null> = useRef(null);
  function set(callback: () => void, delay: number) {
    ref.current = setInterval(callback, delay);
  }
  function clear() {
    if (ref.current) {
      clearInterval(ref.current);
      ref.current = null;
    }
  }
  return { set, clear };
}

useInterval.test.ts:

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

describe('70276930', () => {
  beforeAll(() => {
    jest.useFakeTimers();
  });
  afterAll(() => {
    jest.useRealTimers();
  });
  test('should call callback interval', () => {
    const callback = jest.fn();
    const { result } = renderHook(useInterval);
    result.current.set(callback, 100);
    jest.advanceTimersByTime(1000);
    expect(callback).toBeCalledTimes(10);
  });

  test('should clear interval', () => {
    const callback = jest.fn();
    const { result } = renderHook(useInterval);
    result.current.set(callback, 100);
    jest.advanceTimersByTime(100);
    expect(callback).toBeCalledTimes(1);
    result.current.clear();
    jest.advanceTimersByTime(100);
    expect(callback).toBeCalledTimes(1);
  });
});

测试结果:

 PASS  examples/70276930/useInterval.test.ts (7.541 s)
  70276930
    ✓ should call callback interval (16 ms)
    ✓ should clear interval (1 ms)

----------------|---------|----------|---------|---------|-------------------
File            | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------------|---------|----------|---------|---------|-------------------
All files       |     100 |       50 |     100 |     100 |                   
 useInterval.ts |     100 |       50 |     100 |     100 | 9                 
----------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        8.294 s, estimated 9 s

软件包版本:

"react": "^16.14.0",
"@testing-library/react": "^11.2.2",
"jest": "^26.6.3",

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-26
    • 2021-04-17
    • 2021-03-10
    • 2021-06-07
    • 2020-07-10
    • 2021-12-14
    • 2020-02-19
    • 2020-05-25
    相关资源
    最近更新 更多