javascript宏任务与微任务单元测试关键在于控制执行时机、隔离异步行为、分阶段验证任务顺序:先断言同步输出,再清空微任务队列验证微任务执行,最后推进宏任务验证其触发,需配合fake timers和显式微任务清空(如await vi.runalltasks()或await jest.runallticks())。

JavaScript 中做宏任务与微任务的单元测试断言,关键不是“等任务执行完”,而是控制执行时机、隔离异步行为、验证任务插入顺序和触发时机。直接用 expect(...).toBe(...) 断言异步结果往往失败,因为测试框架默认同步执行完毕就结束——此时 setTimeout 回调还没进队列,Promise.then 还没被推入微任务队列。
用 fake timers 模拟时间推进
主流测试库(Jest、Vitest)都支持 fake timers,能冻结/快进时间,让定时器回调立刻进入宏任务队列,再配合 await waitForNextTick() 或 flushMicrotasks() 触发微任务执行。
- Jest 中启用:
jest.useFakeTimers(),之后调用jest.runAllTimers()或jest.advanceTimersByTime(0)推进时间 - Vitest 中:
vi.useFakeTimers()+vi.runAllTimers()或await vi.runOnlyPendingTimers() - 注意:fake timers 不会自动执行微任务,需额外清空微任务队列(如 Jest 的
jest.runAllTicks(),Vitest 的await vi.runAllTasks())
显式等待微任务清空
微任务不会被 fake timers 自动触发,必须手动确保它们执行。最可靠方式是:
- 在 Jest 中:
await jest.runAllTicks()(等价于清空当前微任务队列) - 在 Vitest 中:
await vi.runAllTasks()(包含微任务 + 宏任务中已排队的微任务) - 或用原生方式:
await Promise.resolve()—— 它会把后续代码推到下一个微任务,但不能清空已有队列;更稳妥的是封装一个flushMicrotasks():
然后 await flushMicrotasks(); 可确保本轮微任务全部执行完毕。
Java开发手册规约集合,基于阿里巴巴Java开发手册(嵩山版)。 涵盖7大维度:编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构、设计规约。 当用户需要:(1) 编写或审查Java代码 (2) 检查命名/代码规范 (3) 处理异常和日志 (4) 编写单元测试 (5) 安全编码 (6) 数据库设...
断言执行顺序要分层验证
不能只看最终输出,要分阶段断言中间状态。例如对如下代码:
console.log('a');setTimeout(() => console.log('b'), 0);Promise.resolve().then(() => console.log('c'));应设计三段断言:
- 同步阶段后:已输出 'a',未输出 'b' 或 'c'
- 清空微任务后:已输出 'a' 和 'c',仍未输出 'b'
- 推进宏任务后:最终输出 'a'、'c'、'b'
可用数组收集日志(如 const logs = []),代替 console.log,再按阶段检查 logs 内容。
避免依赖真实时间,改用 queueMicrotask 或 process.nextTick
测试中尽量少用 setTimeout(..., 0),因为它受 fake timers 控制且语义模糊。更清晰的做法是:
- 用
queueMicrotask(() => {...})显式插入微任务,行为确定、无需 timer 控制 - Node.js 环境下可用
process.nextTick(() => {...})(优先级高于 Promise)做更高阶验证 - 这样测试逻辑更聚焦于任务类型本身,而非定时器调度细节
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










