在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
相关产品推荐
相关产品推荐

