reactjs - 开玩笑 - 在 useEffect 挂钩中测试 clearTimeout
问题描述
在单击按钮组件中,我强制禁用按钮 500 毫秒以防止多次提交,并且在 0.5 秒后禁用状态恢复为默认值。尽管有不同的方法,但我得到了两行代码,我似乎无法在单元测试中涵盖这些代码。
请参阅下面的简化组件源:
import React, {useState, useEffect} from 'react';
const Button = ({disabled, onClick}) => {
const [disableButton, forceDisabledButton] = useState(false);
useEffect(() => {
let timeId;
if (disableButton) {
timeId = setTimeout(() => {
forceDisabledButton(false);
}, 500);
}
return () => {
if (timeId) {
clearTimeout(timeId);
}
}
}, [disableButton]);
const onButtonClick = (e) => {
onClick && onClick(e);
forceDisabledButton(true);
}
return (
<button onClick={onButtonClick} disabled={!disableButton ? disabled : disableButton}>Button</button>
)
}
的默认值disabled
设置为false
。测试用例:
(...)
it('should become disabled after click and then return to its previous disabled state', () => {
const mountButton = shallow(<Button/>);
jest.useFakeTimers();
expect(mountButton.find('button').length).toEqual(1);
mountButton.find('button').simulate('click');
expect(mountButton.find('button').prop('disabled')).toEqual(true);
setTimeout(() => {
expect(mountButton.find('button').prop('disabled')).toEqual(false);
expect(clearTimeout).toHaveBeenCalledWith(expect.any(Number));
}, 600)
})
未被覆盖的行是:forceDisabledButton(false);
和clearTimeout(timeId);
。我jest.runAllTimers()
最初尝试过,但它也没有设法涵盖这两个功能。测试通过并且在应用程序中我没有任何内存泄漏警告(并且视觉确认按钮被禁用 500 毫秒),所以我知道它工作正常并且这两个函数都被调用。我可以尝试在单元测试中解决这两个功能的哪些修改?
谢谢
解决方案
您可以使用runAllTimers
:
it('should become disabled after click and then return to its previous disabled state', (done) => {
const mountButton = mount(<Button/>);
jest.useFakeTimers();
expect(mountButton.find('button').length).toEqual(1);
mountButton.find('button').simulate('click');
expect(mountButton.find('button').prop('disabled')).toEqual(true);
setTimeout(() => {
expect(mountButton.find('button').prop('disabled')).toEqual(false);
done(); // not sure if it's required for case with `runAllTimers`
}, 600);
jest.runAllTimers();
})
或者你可以利用advanceTimersByTime
它来检查延迟是否正好是 500:
it('should become disabled after click and then return to its previous disabled state', () => {
const mountButton = mount(<Button/>);
jest.useFakeTimers();
// ...
jest.advanceTimersByTime(499);
expect(mountButton.find('button').prop('disabled')).toEqual(true);
jest.advanceTimersByTime(2);
expect(mountButton.find('button').prop('disabled')).toEqual(false);
})
至于clearTimeout
作为清理的一部分,useEffect
它将在重新渲染或安装时调用。因此,如果您真的想检查它是否被调用,只需使用mountButton.update()
. 但是您可以只验证是否clearTimeout
被调用,而不是检查它是否已作为useEffect
钩子的一部分被调用。
runOnlyPendingTimers
一般来说,使用over更安全,因为如果我们有顺序输入,runAllTimers
以后一次可能会导致无限循环(但不是这种情况)setTimeout
useEffect
[UPD]shallow()
可能无法正常工作,因为与钩子集成仍然存在未解决的问题。
推荐阅读
- c# - 实体框架在查询中创建不存在的列
- java - Java8 Stream api比较列表
- angular - 创建新模块时抛出对装饰器的实验性支持错误
- python - 将数据帧存储为 csv 格式的问题
- python - Kivy Image Widget - 模块对象不可调用
- python - 安装 Setuptools-scm Pypi 时出现错误为“ValueError:Zip 不支持 1980 年之前的时间戳”
- php - Symfony Doctrine 两个用于不同目的的键,一个 AUTO_INCREMENT
- mapbox - 检查 MapBOX 的多边形/边界中是否加载了所有图块/特征
- java - 将 OpenAPI 2.0 文件的上传按钮添加为 Octet-Stream
- node.js - 为什么现有模型中未应用 Mongoose 默认值?