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

在Jest中测试带定时器的Web Worker:如何接收正确的倒计时消息?

在Jest中测试带定时器的Web Worker:如何接收正确的倒计时消息?

嘿,我来帮你搞定这个Web Worker的Jest测试问题!先把你写的Worker代码补全末尾的小疏漏,完整版本如下:

export const timerWorker = () => {
    let timerStart = false;
    let intervalId = null;
    let timeLeft = 0;
    
    self.onmessage = (e) => {
        const { data = {} } = e;
        const { action, payload } = data;
        
        switch (action) {
            case 'start':
                start(payload);
                break;
            case 'stop':
                stop();
                break;
            default:
                self.postMessage('please pass a valid action');
                break;
        }
    };

    function start(payload) {
        const { interval = 1000, duration = 0 } = payload;
        stop();
        timeLeft = duration;
        if (duration >= 0) self.postMessage(timeLeft);
        intervalId = setInterval(function () {
            timeLeft -= interval;
            if (timeLeft <= 0) {
                stop();
            } else {
                self.postMessage(timeLeft);
            }
        }, interval);
        timerStart = true;
    }

    function stop() {
        if (intervalId) {
            clearInterval(intervalId);
            intervalId = null;
        }
        timerStart = false;
    }
};

接下来咱们一步步写测试,核心思路是用Jest的假定时器模拟时间流逝,同时监听Worker的postMessage事件来验证倒计时消息,具体步骤和代码如下:

测试核心逻辑拆解

  • 模拟Web Worker的全局上下文self,因为Jest默认没有Worker环境
  • 启用Jest假定时器,避免真实等待倒计时浪费时间
  • 给self.postMessage做Mock,捕获Worker发送的所有消息
  • 触发Worker的start/stop动作,手动推进时间验证消息正确性

完整测试代码示例

import { timerWorker } from './your-worker-file-path'; // 替换成你的Worker文件路径

describe('Timer Web Worker', () => {
  beforeEach(() => {
    // 重置假定时器和所有Mock
    jest.useFakeTimers();
    jest.clearAllMocks();
    
    // 模拟Web Worker的self全局对象
    global.self = {
      postMessage: jest.fn(),
      onmessage: null
    };
    
    // 初始化Worker逻辑
    timerWorker();
  });

  afterEach(() => {
    // 恢复真实定时器,清理全局模拟对象
    jest.useRealTimers();
    delete global.self;
  });

  it('should send correct countdown messages during timer running', () => {
    // 给Worker发送start指令:总时长5000ms,每秒更新一次
    global.self.onmessage({
      data: {
        action: 'start',
        payload: { duration: 5000, interval: 1000 }
      }
    });

    // 初始时刻应该发送5000
    expect(global.self.postMessage).toHaveBeenCalledWith(5000);

    // 手动推进1秒,验证是否发送4000
    jest.advanceTimersByTime(1000);
    expect(global.self.postMessage).toHaveBeenCalledWith(4000);

    // 再推进3秒,验证是否发送1000
    jest.advanceTimersByTime(3000);
    expect(global.self.postMessage).toHaveBeenCalledWith(1000);

    // 再推进1秒,此时倒计时结束,不会再发送新消息
    jest.advanceTimersByTime(1000);
    // 检查总共发送了3次消息:5000、4000、1000
    expect(global.self.postMessage).toHaveBeenCalledTimes(3);
  });

  it('should stop sending messages when receiving stop action', () => {
    global.self.onmessage({
      data: {
        action: 'start',
        payload: { duration: 5000, interval: 1000 }
      }
    });

    // 推进2秒,验证发送3000
    jest.advanceTimersByTime(2000);
    expect(global.self.postMessage).toHaveBeenCalledWith(3000);

    // 发送stop指令停止定时器
    global.self.onmessage({ data: { action: 'stop' } });

    // 再推进2秒,确认不会发送1000的消息
    jest.advanceTimersByTime(2000);
    expect(global.self.postMessage).not.toHaveBeenCalledWith(1000);
  });

  it('should return error message for invalid action', () => {
    // 发送不存在的指令
    global.self.onmessage({
      data: { action: 'invalid-action' }
    });

    expect(global.self.postMessage).toHaveBeenCalledWith('please pass a valid action');
  });
});

测试注意事项

  • 每个测试前一定要重置self的Mock和假定时器,避免测试之间的状态污染
  • 推进时间时要和Worker设置的interval对应,保证消息触发时机正确
  • 记得覆盖边界场景,比如duration=0时Worker直接发送0并停止的情况

备注:内容来源于stack exchange,提问作者Atul Dwivedi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 14:29:52