【问题标题】:How to mock callback function used in hook which is bound with useRef?如何模拟与useRef绑定的钩子中使用的回调函数?
【发布时间】:2021-06-01 17:56:55
【问题描述】:

我正在尝试模拟回调函数(cb)并想检查是否被调用了 1 次。

我试过了:

    jest.useFakeTimers()
    const cb = (t: number) => `message-${t}`
    const spy = jest.fn(cb)
    // This one also fails.
    // jest.spyOn(React, 'useRef').mockReturnValue({ current: cb });
    const { result } = renderHook(() => useTimer(3, cb))

    await waitFor(() => expect(result.current[0]).toBe(3))
    await waitFor(() => expect(result.current[1]).toBe('message-3'))
    await waitFor(() => expect(spy).toBeCalledTimes(1)) // received 0
const useTimer = (
  startTimeSec: number,
  cb: (currentSeconds: number) => string = (c) => '',
): [number, string] => {
  const [timer, setTimer] = useState(startTimeSec)
  const [intervalTime, setIntervalTime] = useState<null | number>(null)
  const [message, setMessage] = useState<string>('')
  const callback = React.useRef(cb)

  useEffect(() => {
    if (startTimeSec) {
      setTimer(startTimeSec)
      setMessage(callback.current(timer))
    }
  }, [startTimeSec])

  //...

  return [timer, message]
}

有什么想法吗?

【问题讨论】:

    标签: reactjs typescript jestjs mocking react-hooks


    【解决方案1】:

    您应该将模拟的回调函数spy 传递给useTimers 而不是cb

    另外,让我们回忆一下useRef()的概念:

    useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传递的参数 (initialValue)。

    这意味着您模拟的 cb 函数将是 .current 属性的值。

    例如

    userTimer.ts:

    import React, { useEffect, useState } from 'react';
    
    const useTimer = (startTimeSec: number, cb: (currentSeconds: number) => string = (c) => ''): [number, string] => {
      const [timer, setTimer] = useState(startTimeSec);
      const [intervalTime, setIntervalTime] = useState<null | number>(null);
      const [message, setMessage] = useState<string>('');
      const callback = React.useRef(cb);
    
      useEffect(() => {
        if (startTimeSec) {
          setTimer(startTimeSec);
          setMessage(callback.current(timer));
        }
      }, [startTimeSec]);
    
      return [timer, message];
    };
    
    export { useTimer };
    

    useTimer.test.ts:

    import { waitFor } from '@testing-library/react';
    import { renderHook } from '@testing-library/react-hooks';
    import { useTimer } from './useTimer';
    
    describe('useTimer', () => {
      it('should pass', async () => {
        const cb = (t: number) => `message-${t}`;
        const spy = jest.fn(cb);
        const { result } = renderHook(() => useTimer(3, spy));
    
        await waitFor(() => expect(result.current[0]).toBe(3));
        await waitFor(() => expect(result.current[1]).toBe('message-3'));
        await waitFor(() => expect(spy).toBeCalledTimes(1));
      });
    });
    

    单元测试结果:

     PASS  examples/66452119/useTimer.test.ts
      useTimer
        ✓ should pass (20 ms)
    
    -------------|---------|----------|---------|---------|-------------------
    File         | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
    -------------|---------|----------|---------|---------|-------------------
    All files    |   92.31 |    33.33 |   66.67 |     100 |                   
     useTimer.ts |   92.31 |    33.33 |   66.67 |     100 | 3-10              
    -------------|---------|----------|---------|---------|-------------------
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   0 total
    Time:        5.931 s
    

    【讨论】:

      猜你喜欢
      • 2021-04-02
      • 2021-04-01
      • 2021-12-03
      • 2019-04-12
      • 2022-10-12
      • 2020-12-18
      • 2021-08-16
      • 2021-02-23
      • 1970-01-01
      相关资源
      最近更新 更多