其他
异步测试不稳定时,用条件轮询替代固定等待时间
当测试因竞态、时序依赖或负载波动而时好时坏时,改为轮询等待真正关心的条件,消除不稳定的固定延迟。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
异步测试不稳定时,用条件轮询替代固定等待通用
解决思路
- 核心原则:等待你真正关心的实际条件成立,而不是猜测需要多长时间。
- 识别需要改造的测试:使用了 setTimeout、sleep、time.sleep() 等任意延迟的测试。
- 对频繁出现 flaky(有时通过、负载下失败)、并行运行时超时、或需要等待异步操作完成的测试套件进行改造。
- 把「先固定延迟再断言」改为「先等待条件成立再断言」:将 await new Promise(r => setTimeout(r, 50)) 替换为 await waitFor(() => getResult() !== undefined)。
- 按场景选择条件:等事件用 waitFor(() => events.find(e => e.type === 'DONE'));等状态用 waitFor(() => machine.state === 'ready');等数量用 waitFor(() => items.length >= 5);等文件用 waitFor(() => fs.existsSync(path));复杂条件用 waitFor(() => obj.ready && obj.value > 10)。
- 实现通用轮询函数 waitFor:接收 condition、description、timeoutMs(默认 5000),循环调用 condition,有返回值就返回该值。
- 在轮询函数中检查超时:当 Date.now() - startTime > timeoutMs 时抛出明确错误,如 Timeout waiting for ${description} after ${timeoutMs}ms。
- 在轮询函数中每 10ms 轮询一次:await new Promise(r => setTimeout(r, 10))。
- 按领域需求编写专用辅助函数,例如 waitForEvent、waitForEventCount、waitForEventMatch。
- 避免轮询过快:不要用 setTimeout(check, 1),会浪费 CPU;应每 10ms 轮询一次。
- 避免无超时:不要无限循环等待永不满足的条件;始终设置超时并给出清晰错误。
- 避免陈旧数据:不要在循环外缓存状态;应在循环内调用 getter 以获取最新数据。
- 当确实需要测量真实时序行为时(如工具每 100ms tick 一次、需 2 个 tick 验证部分输出),先用 waitForEvent(manager, 'TOOL_STARTED') 等待触发条件,再用 await new Promise(r => setTimeout(r, 200)) 等待计时行为,并注明 200ms = 两个 100ms tick 的原因。
- 保留任意超时必须满足三条要求:先等待触发条件;基于已知时序而非猜测;用注释解释为什么需要该延迟。
适用边界 · 注意事项
- 不要用于测试真实的时序行为,例如 debounce、throttle 间隔。
- 若确实使用任意超时,必须记录为什么需要它,否则不应保留。