测试带缓存的异步函数需验证缓存命中/未命中路径、生命周期控制、并发安全及超时重试协同:首次调用执行真实请求,后续复用缓存;并发调用应仅触发一次底层操作;缓存失效、清除、多参数隔离须准确;超时失败不缓存,重试成功才缓存,命中拒绝态Promise不重试。

测试带有缓存机制的异步函数,关键在于验证缓存是否真正生效、是否正确复用、以及在边界条件下(如缓存失效、并发调用、错误重试)行为是否符合预期。不能只测“能返回结果”,而要测“怎么返回”。
验证缓存命中与未命中的执行路径
缓存机制的核心逻辑是:首次调用执行真实异步操作,后续调用直接返回缓存值。需通过模拟或拦截手段区分这两条路径:
- 用 spy 或 mock 替换底层异步依赖(如
fetch、axios.get),并统计调用次数;首次应被调用,第二次应为 0 次 - 在被测函数内部添加可检测的副作用(如写入全局计数器或触发自定义事件),确保二次调用不触发该副作用
- 对返回的 Promise 实例做
Object.is()对比:若缓存的是同一 Promise 实例(常见于内存缓存),两次await fn()的 Promise 应严格相等;若缓存的是 resolved 值,则 Promise 不同但结果相同
控制缓存生命周期与状态
真实缓存往往带 TTL、最大容量或手动清除逻辑。测试时需主动干预其状态:
- 手动设置缓存过期时间(如修改内部
cacheExpireTime或注入可操控的Date.now),验证超时后是否重新发起请求 - 显式调用
clearCache()或类似方法后,再次调用应触发新请求 - 模拟多参数调用,验证不同参数组合是否独立缓存(避免 key 冲突)、相同参数是否共享缓存
覆盖并发与竞态场景
多个同步调用同一缓存函数时,若未加锁,可能重复触发底层请求(即“缓存击穿”)。这是高频出错点:
立即学习“Java免费学习笔记(深入)”;
- 并发发起 5 次相同调用,底层异步操作应仅执行 1 次(而非 5 次),且所有 Promise 都 resolve 到相同结果
- 使用
Promise.all([fn(), fn(), fn()])并断言底层 fetch 被调用 1 次 - 若缓存实现用了
Promise.resolve(cacheValue)直接返回,需确认它不会在 resolve 后又被重新计算——这通常靠闭包或 WeakMap 锁定初始 Promise 实例
结合超时与重试的复合逻辑验证
当缓存函数叠加了 promiseTimeout 或 withRetry 时,测试重点转向策略协同:
- 缓存未命中 + 请求超时 → 应进入 timeout 的 reject 分支,且该失败不应写入缓存(避免缓存错误)
- 缓存未命中 + 请求失败 + 重试成功 → 最终 resolve,且结果应被缓存;重试过程中的中间 reject 不应暴露给调用方
- 缓存命中 + 手动模拟缓存值为 rejected Promise → 应原样抛出,不触发重试(因重试属于“获取阶段”,非“读取阶段”)


















