使用Jest测试切换按钮的setInterval与clearInterval功能
定时器切换功能的Jest测试问题
我实现了一个功能:点击切换按钮,当按钮处于开启(checked)状态时,调用setInterval每5秒执行一次someFunction;再次点击切换至关闭状态时,调用clearInterval停止执行。功能运行正常,但编写Jest测试用例时不确定写法是否正确,求指导。
项目代码
app.ts
let toggleButton: HTMLInputElement; let interval; toggleButton = <HTMLInputElement>document.getElementById("toggle_switch"); toggleButton.addEventListener("change", (e) => { const target = e.target as HTMLInputElement; if (target.checked) { try { interval = setInterval(function () { return someFunction(); }, 5000); } catch (error) { console.log(error); } } else { clearInterval(interval); } }); // 假设存在someFunction定义 function someFunction() { /* ... */ }
当前测试代码(app.test.ts)
import {toggleButton} from "./app"; jest.useFakeTimers(); jest.spyOn(global, "setInterval"); describe("Testing if interval is set or clear on button click", () => { test("When toggle switch is on", () => { toggleButton.click(); expect(setInterval).toBeCalled(); }); test("When toggle switch is off", () => { toggleButton.click(); expect(clearInterval).toBeCalled(); }); });
当前测试的问题
- DOM元素未模拟:测试环境无真实DOM,直接导入
toggleButton会得到null,触发报错。 - 关闭逻辑测试错误:按钮默认处于未选中状态,直接点击会切换为选中,触发
setInterval而非clearInterval,无法验证关闭分支。 - 未监听
clearInterval:仅对setInterval做了spy,clearInterval无监听,expect(clearInterval).toBeCalled()会失效。 - 测试隔离性差:全局
interval变量会在测试间共享,导致测试互相干扰。
修正后的测试用例
// 模拟DOM环境,确保getElementById能获取到元素 beforeEach(() => { document.body.innerHTML = '<input type="checkbox" id="toggle_switch">'; // 重置模块,确保每次测试都能获取新的DOM元素引用 jest.resetModules(); require('./app'); }); afterEach(() => { jest.clearAllTimers(); jest.restoreAllMocks(); }); jest.useFakeTimers(); describe("Testing if interval is set or clear on button click", () => { test("When toggle switch is turned on, setInterval is called", () => { const toggleButton = document.getElementById("toggle_switch") as HTMLInputElement; const setIntervalSpy = jest.spyOn(global, "setInterval"); // 初始未选中,点击后切换为选中 toggleButton.click(); expect(toggleButton.checked).toBe(true); expect(setIntervalSpy).toHaveBeenCalledTimes(1); // 验证定时器间隔和回调函数 expect(setIntervalSpy).toHaveBeenCalledWith(expect.any(Function), 5000); }); test("When toggle switch is turned off, clearInterval is called", () => { const toggleButton = document.getElementById("toggle_switch") as HTMLInputElement; const mockIntervalId = 123 as unknown as NodeJS.Timeout; const setIntervalSpy = jest.spyOn(global, "setInterval").mockReturnValue(mockIntervalId); const clearIntervalSpy = jest.spyOn(global, "clearInterval"); // 先开启按钮,触发setInterval toggleButton.checked = true; toggleButton.dispatchEvent(new Event("change")); // 再关闭按钮,触发clearInterval toggleButton.checked = false; toggleButton.dispatchEvent(new Event("change")); expect(clearIntervalSpy).toHaveBeenCalledTimes(1); // 验证传入的定时器ID正确 expect(clearIntervalSpy).toHaveBeenCalledWith(mockIntervalId); }); });
关键修正点
- 模拟DOM元素:在
beforeEach中创建测试所需的按钮,确保getElementById正常工作。 - 修正关闭测试逻辑:先将按钮设为选中状态并触发change事件,再切换为未选中触发事件,确保进入
clearInterval分支。 - 监听
clearInterval:对clearInterval添加spy,确保能验证调用情况。 - 测试隔离:用
beforeEach和afterEach重置模块、清理定时器和mock,避免测试间互相干扰。
内容的提问来源于stack exchange,提问作者Fewelebele
相关产品推荐
相关产品推荐

