reactjs - 在 Jest/Enzyme 测试期间无法运行 React Hook setInterval Timer

原文 标签 reactjs jestjs react-hooks enzyme create-react-app

我正在尝试为这个组件编写一些测试。我实现了一个自定义 useInterval关注 Dan Abramov (code) 的这篇博文。它基本上是声明性的 setInterval功能。但是,我很难用 Jest 和 Enzyme 对其进行测试。该应用程序是使用 create-react-app 引导的。我已经安装了最新版本。当我单击开始按钮时,耗时会增加并完美地显示在页面上。在父组件中,我更新了存储耗时的状态。它通过了更新的耗时timer支持 Timer.js .因此,它在屏幕上播下了正确的 elapsedTime .这在浏览器中按预期工作。但是,运行测试时计时器不会运行。

// Timer.js
const TimerDisplay = ({ timer, updateTimer }) => {

  useInterval(() => {
    const delta = Math.floor((Date.now() - timer.offsetTime) / 1000);
    updateTimer({ ...timer, elapsedTime: delta });

  }, timer.isTicking ? 300 : null);

  const handleStartButton = () => {
    updateTimer({ ...timer, isTicking: true });
  }

  return (
    <React.Fragment>
      <div>{timer.elapsedTime}</div>
      <button className='start' onClick={() => handleStartButton()}>Start</button>
      <button className='stop' {/* removed for brevity*/}>Stop</button>
    </React.Fragment>
  );
};

测试代码如下。我使用 Jest 的 spy 功能和 enzyme 的坐骑。我读到我需要安装而不是因为钩子(Hook)而使用浅层。我设置 Jest 使用假计时器。然后,我模拟按下开始按钮的按钮,以验证按钮是否正常工作。但是,在这个测试中,我已经设置了 isTicking: true所以我真的不需要模拟开始。尽管功能 spy 按预期工作是一个健全的检查 - 它确实如此。预期的结果是它在 300 毫秒后调用了 spy 回调。因此,当我 jest.advanceTimersByTime(500) , spy 函数应该在 useInterval 中至少被调用一次。打回来。但是,这在测试中没有发生。
// Timer.spec.js
describe('Timer', () => {
  const spyFunction = jest.fn();

  const timer = {
    offsetTime: new Date(),
    isTicking: true,
    elapsedTime: 0
  };

  let wrapper = null;

  beforeEach(() => {
    wrapper = mount(<Timer updateTimer={spyFunction} timer={timer} />);
  });

  afterEach(() => {
    wrapper.unmount();
    wrapper = null;
  });

  it('timer should run', () => {
    jest.useFakeTimers();

    expect(spyFunction).not.toHaveBeenCalled();

    wrapper.find('button.start').simulate('click', { button: 0 });

    // works as expected
    expect(spyFunction).toHaveBeenCalled();

    // doesn't seem to work for some reason
    jest.advanceTimersByTime(500);

    // will fail; function only called once by the button.start. It should have been called at least twice.
    expect(spyFunction).toHaveBeenCalledTimes(2);
  });
});

我认为这个问题与 useInterval 有关钩。我怀疑在能够调用回调之前进行垃圾收集。有什么方法可以测试useInterval钩子(Hook)回调的调用updateTimer又名 Jest.Fn ?
// useInterval hook
import { useEffect, useRef } from 'react';

export const useInterval = (callback, delay) => {
  const savedCallback = useRef();

  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);

  useEffect(() => {
    function tick() {
      savedCallback.current();
    }
    if (delay !== null) {
      let id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
};

最佳答案

问题在于 jest.useFakeTimers(); ,更具体地说,在你称之为它的地方。您正在将组件安装到 beforeEach ,在你伪造计时器之前。因此,您的 useInterval Hook 设置为使用真实 setInterval ,而不是 Jest 伪造。您调用jest.useFakeTimers();的那一刻在测试中为时已晚,因为它不会影响任何事情(您没有创建与计时器相关的任何新内容)。

如果在 mount 之前移动它在 beforeEach测试会起作用。

关于reactjs - 在 Jest/Enzyme 测试期间无法运行 React Hook setInterval Timer,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61923449/

相关文章:

reactjs - Next.js 与 SCSS 拆分为每个组件的文件

node.js - TypeError : require. requireActual不是函数

javascript - 过滤react-multi-select时如何调用服务器

javascript - 在 React 中传递变量引用?

html - React Js数学问题

angular - 使用 { dispatch : false } 进行 NGRX 效果测试

javascript - Jest : How to globally mock node-uuid (or any other imported module)

javascript - 如何在 React Native 中制作带有钩子(Hook)的无限图像轮播

javascript - react :应用程序多次请求相同的端点

javascript - 使用单个 handleInputChange 方法来处理多个输入字段(React)