用途
Condition-Based Waiting(基于条件的等待)用于解决异步测试中的偶发失败问题。资料指出,不稳定的测试常常用任意延迟来猜测时间,这会引入竞态条件:测试在快速机器上通过,但在高负载或 CI 环境中失败。其核心原则是:等待你真正关心的实际条件,而不是猜测它需要多长时间。
何时使用
资料用决策图说明判断逻辑:如果测试使用了 setTimeout/sleep,则进一步判断是否在测试计时行为;如果是测试计时行为,应记录为什么需要超时;如果不是,则应使用基于条件的等待。
适用场景:
- 测试中存在任意延迟(setTimeout、sleep、time.sleep())
- 测试不稳定(有时通过,负载下失败)
- 测试在并行运行时超时
- 等待异步操作完成
不适用场景:
- 测试真实的计时行为(防抖、节流间隔)
- 如果使用任意超时,始终要记录原因
主要能力与核心模式
资料给出前后对比示例:
// ❌ BEFORE: Guessing at timing
await new Promise(r => setTimeout(r, 50));
const result = getResult();
expect(result).toBeDefined();
// ✅ AFTER: Waiting for condition
await waitFor(() => getResult() !== undefined);
const result = getResult();
expect(result).toBeDefined();
常见等待模式:
| 场景 | 模式 |
|---|---|
| 等待事件 | 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) |
使用方式
资料提供一个通用轮询函数实现:
async function waitFor<T>(
condition: () => T | undefined | null | false,
description: string,
timeoutMs = 5000
): Promise<T> {
const startTime = Date.now();
while (true) {
const result = condition();
if (result) return result;
if (Date.now() - startTime > timeoutMs) {
throw new Error(`Timeout waiting for ${description} after ${timeoutMs}ms`);
}
await new Promise(r => setTimeout(r, 10)); // Poll every 10ms
}
}
资料还提到可参考 @example.ts 获取完整实现,其中包含来自实际调试会话的领域专用辅助函数(waitForEvent、waitForEventCount、waitForEventMatch)。
常见错误
- ❌ 轮询过快:setTimeout(check, 1) —— 浪费 CPU;✅ 修正:每 10ms 轮询一次
- ❌ 没有超时:条件永不满足时会无限循环;✅ 修正:始终包含超时并给出清晰错误
- ❌ 数据陈旧:在循环前缓存状态;✅ 修正:在循环内调用 getter 以获取最新数据
何时任意超时是正确的
资料给出示例:
// Tool ticks every 100ms - need 2 ticks to verify partial output
await waitForEvent(manager, 'TOOL_STARTED'); // First: wait for condition
await new Promise(r => setTimeout(r, 200)); // Then: wait for timed behavior
// 200ms = 2 ticks at 100ms intervals - documented and justified
要求:
- 首先等待触发条件
- 基于已知计时(而非猜测)
- 用注释解释原因
实际效果
资料记录了一次调试会话(2025-10-03)的结果:
- 修复了 3 个文件中的 15 个不稳定测试
- 通过率:60% → 100%
- 执行时间:快 40%
- 不再有竞态条件
限制
资料未提供该方法的安装命令、依赖要求、支持的语言或框架范围、许可与收费信息,这些内容暂未提供。