reactjs - 用 setTimeout 测试钩子状态更新
问题描述
我正在尝试使用单击处理程序测试自毁组件的卸载。点击处理程序使用 setTimeout 更新 useState。
但是,我的测试失败了,而我希望它通过。我尝试使用诸如 AdvanceTimersByTime() 之类的笑话模拟计时器,但它不起作用。如果我在 setTimeout 之外调用 setState,则测试通过。
组件.js
const DangerMsg = ({ duration, onAnimDurationEnd, children }) => {
const [isVisible, setVisible] = useState(false);
const [sectionClass, setSectionClass] = useState(classes.container);
function handleAnimation() {
setSectionClass(classes.containerAnimated);
let timer = setTimeout(() => {
setVisible(false);
}, 300);
return clearTimeout(timer);
}
useEffect(() => {
let timer1;
let timer2;
function animate() {
if (onAnimDurationEnd) {
setSectionClass(classes.containerAnimated);
timer2 = setTimeout(() => {
setVisible(false);
}, 300);
} else {
setVisible(false);
}
}
if (children) {
setVisible(true);
}
if (duration) {
timer1 = setTimeout(() => {
animate();
}, duration);
}
return () => {
clearTimeout(timer1);
clearTimeout(timer2);
};
}, [children, duration, onAnimDurationEnd]);
return (
<>
{isVisible ? (
<section className={sectionClass} data-test="danger-msg">
<div className={classes.inner}>
{children}
<button
className={classes.btn}
onClick={() => handleAnimation()}
data-test="btn"
>
×
</button>
</div>
</section>
) : null}
</>
);
};
导出默认的 DangerMsg;
测试.js
it("should NOT render on click", async () => {
jest.useFakeTimers();
const { useState } = jest.requireActual("react");
useStateMock.mockImplementation(useState);
// useEffect not called on shallow
component = mount(
<DangerMsg>
<div></div>
</DangerMsg>
);
const btn = findByTestAttr(component, "btn");
btn.simulate("click");
jest.advanceTimersByTime(400);
const wrapper = findByTestAttr(component, "danger-msg");
expect(wrapper).toHaveLength(0);
});
注意,我用实际来模拟 useState 实现,因为在其他测试中我使用了自定义 useState 模拟。
解决方案
不使用酶,testing-library/react
而是使用部分解决方案。以下测试按预期通过:
test("display loader after 1 second", () => {
jest.useFakeTimers(); // mock timers
const { queryByRole } = render(
<AssetsMap {...defaultProps} isLoading={true} />
);
act(() => {
jest.runAllTimers(); // trigger setTimeout
});
const loader = queryByRole("progressbar");
expect(loader).toBeTruthy();
});
我直接运行计时器,但按时间推进应该会给出类似的结果。
推荐阅读
- r - 如何在R中循环遍历mapply?
- swift - 如何在 Firestore 中使用文档 ID 创建复合索引
- java - 如何在java中每秒运行60次代码
- node.js - 在我的 slackbot 应用程序中,函数被节点执行两次
- javascript - 如何让全局变量留在角度构造函数和承诺中,调用需要来自 diff 组件的数据的函数
- javascript - 自定义设置方法不起作用(javascript)
- android - Android 运行时错误:芯片膨胀期间发生的 ID 0x00000001 无效
- apache-spark-sql - 在 Spark SQL 中使用字符串变量作为日期过滤观察结果
- android - 我必须有一个结算帐户才能使用 Google Maps Api 密钥吗?
- python - 有没有办法在 Django 模型中拥有一个列表类型的对象?