如何使用Jest测试包含setInterval与process.exit调用的类方法
解决方案
问题根源
- 无效输入校验场景下,空参数触发的
process.exit(100)是同步执行逻辑,原写法混用同步调用和done异步回调,会导致Jest提前判定用例执行结束,不等回调触发。 - 有效输入执行场景下,
setInterval属于异步宏任务,没有手动触发定时器执行的情况下,回调永远不会运行,process.exit(0)永远不会被调用,因此断言不会触发。 - 用例之间没有清理mock和定时器,会导致不同用例的逻辑互相干扰。
正确测试实现
借助Jest内置的假定时器能力管控定时任务,清理用例间的环境依赖即可解决问题,完整测试代码如下:
import Subject from "../src/subject.class"; describe("subject test suite", () => { // 每个用例执行前初始化测试环境 beforeEach(() => { // 启用假定时器替换真实定时器 jest.useFakeTimers(); // mock console.log避免日志输出污染测试结果 jest.spyOn(console, "log").mockImplementation(); }) // 每个用例执行后清理环境,避免用例间互相干扰 afterEach(() => { // 恢复真实定时器 jest.useRealTimers(); // 恢复所有被mock的方法 jest.restoreAllMocks(); }) it("should exit on invalid input", () => { const spyExit = jest.spyOn(process, "exit").mockImplementation(jest.fn()); new Subject().doWork(""); // 直接断言process.exit被调用时传入的参数为100 expect(spyExit).toHaveBeenCalledWith(100); }); it("should work on valid input", () => { const spyExit = jest.spyOn(process, "exit").mockImplementation(jest.fn()); new Subject().doWork("valid"); // 执行所有待处理的定时器回调,直到没有剩余定时任务 jest.runAllTimers(); // 断言执行完所有定时逻辑后,process.exit被调用时传入的参数为0 expect(spyExit).toHaveBeenCalledWith(0); }); });
额外说明
jest.runAllTimers()会递归执行所有待处理的定时器回调,你代码中setInterval执行到1000次就会调用process.exit,不会出现死循环问题。如果需要更精准的时间控制,也可以替换为jest.advanceTimersByTime(1000)主动推进时间。
内容的提问来源于stack exchange,提问作者Joel
相关产品推荐
相关产品推荐

