reactjs - 无法让 React Hook setInterval 计时器在 Jest/Enzyme 测试期间运行

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

我正在尝试总结为此组件编写一些测试。我按照 Dan Abramov ( code ) 的这篇博客文章实现了一个自定义 useInterval Hook 。它基本上是一个声明性的 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 Hook 有关。我怀疑在调用回调之前就发生了垃圾收集。有没有办法测试 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(); 的那一刻就太晚了,因为它不会影响任何东西(您不会创建任何与计时器相关的新内容)。

如果您在 beforeEach 中的 mount 之前将其移动,则测试将起作用。

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

相关文章:

javascript - 如何将表单中的变量传递给 React.js 中的 onclick 函数?

javascript - 关于 react.js 条件渲染,或者 react.js 中的潜在错误?

node.js - 如何使用 Jest 为这些场景创建用于无服务器测试的测试?

reactjs - React Hook 依赖项 - 通用 Fetch Hook

reactjs - react 使用上下文。无法解构 'currentChatbotInEdit' 的属性 'Object(...)(...)',因为它未定义。我该如何解决?

javascript - 使用 React Hooks 将输入传递给子组件来更新输入值

javascript - 现在有必要配置React.js环境(Webpack,Babel)吗?

material-ui - 使用 Jest 和快照测试 material-ui 组件

reactjs - 模拟 ('change' 之后状态未更新

reactjs - 如何为React应用程序设置kubernetes探针?