我正在尝试总结为此组件编写一些测试。我按照 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/