You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 21:09:32