开玩笑:setTimeout 被调用了太多次
Posted
技术标签:
【中文标题】开玩笑:setTimeout 被调用了太多次【英文标题】:Jest: setTimeout is being called too many times 【发布时间】:2019-09-11 06:38:27 【问题描述】:我正在测试一个使用setTimeout
的react
组件。 问题是 Jest
说 setTimeout
被调用,即使它显然不是。有一个 setTimeout
可以从 ui 中删除某些内容,另一个可以暂停计时器鼠标悬停在组件上。
我尝试在setTimeout
所在的位置添加console.log()
,并且从不调用控制台日志,这意味着未调用应用程序中的setTimeout。
//app
const App = (props) =>
const [show, setShow] = useState(true);
const date = useRef(Date.now());
const remaining = useRef(props.duration);
let timeout;
useEffect(() =>
console.log('Should not run');
if (props.duration)
timeout = setTimeout(() =>
setShow(false)
, props.duration);
, [props.duration]);
const pause = () =>
remaining.current -= Date.now() - date.current;
clearTimeout(timeout);
const play = () =>
date.current = Date.now();
clearTimeout(timeout);
console.log('should not run');
timeout = setTimeout(() =>
setIn(false);
, remaining.current);
return (
<div onMouseOver=pause onMouseLeave=play>
show &&
props.content
</div>
)
//test
it('Should not setTimeout when duration is false', () =>
render(<Toast content="" duration=false />);
//setTimeout is called once but does not come from App
expect(setTimeout).toHaveBeenCalledTimes(0);
);
it('Should pause the timer when pauseOnHover is true', () =>
const container = render(<Toast content="" pauseOnHover=true />);
fireEvent.mouseOver(container.firstChild);
expect(clearTimeout).toHaveBeenCalledTimes(1);
fireEvent.mouseLeave(container.firstChild);
//setTimeout is called 3 times but does not come from App
expect(setTimeout).toHaveBeenCalledTimes(1);
);
所以在第一个测试中,setTimeout
不应该被调用,但我收到它调用一次。在第二个测试中,setTimeout
应该被调用一次,但被调用了 3 次。 该应用程序运行良好我只是不明白jest
发生了什么,这表明setTimeout
被调用的次数超过是的。
【问题讨论】:
这里还有解决方案吗?... 【参考方案1】:我的第一个 Jest 测试总是调用 setTimeout
一次(没有我的组件触发它),我遇到了完全相同的问题。通过记录这个“未知”setTimeout
调用的参数,我发现它是用_flushCallback
函数和0
延迟调用的。
查看react-test-renderer
的存储库显示_flushCallback
函数定义为here。 Scheduler
其中_flushCallback
是其中的一部分,明确指出它在非DOM 环境中运行时使用setTimeout
(在进行Jest 测试时就是这种情况)。
我不知道如何正确地继续研究这个问题,目前看来,测试 setTimeout
被调用的次数似乎不可靠。
【讨论】:
谢谢你;我用你发现的东西创建了一个(一起破解的)解决方法,发布在下面。【参考方案2】:感谢@thabemmz 研究此问题的原因,我有一个共同解决方案:
function countSetTimeoutCalls()
return setTimeout.mock.calls.filter(([fn, t]) => (
t !== 0 ||
!String(fn).includes('_flushCallback')
));
用法:
// expect(setTimeout).toHaveBeenCalledTimes(2);
// becomes:
expect(countSetTimeoutCalls()).toHaveLength(2);
代码在做什么应该很清楚;它过滤掉所有看起来像是来自 react-test-renderer
行的调用(即函数包含 _flushCallback
并且超时为 0。
react-test-renderer
的行为(甚至函数命名)的变化很脆弱,但至少现在可以解决问题。
【讨论】:
以上是关于开玩笑:setTimeout 被调用了太多次的主要内容,如果未能解决你的问题,请参考以下文章
带有 Typescript 错误的玩笑:超时 - 在 jest.setTimeout.Timeout 指定的 5000 毫秒超时内未调用异步回调
setTimeout()和setInterval() 何时被调用执行(非多线程).RP