SuperAPI AI 工具集

condition-based-waiting

测试与质量

查看项目

更新于 10-15 14:47

技能介绍

用途

Condition-Based Waiting 是一个用于编写可靠异步测试的技能。它针对的问题是:不稳定的测试常常用任意延迟来猜测时序,这会引入竞态条件——测试在快速机器上通过,但在负载较高或 CI 环境中失败。

其核心原则是:等待你真正关心的实际条件,而不是猜测它需要多长时间。

何时使用

适用场景:

  • 测试中存在任意延迟(setTimeout、sleep、time.sleep())
  • 测试不稳定(有时通过,在负载下失败)
  • 测试在并行运行时超时
  • 需要等待异步操作完成

不适用场景:

  • 测试真实的时序行为(防抖、节流间隔)
  • 如果确实使用任意超时,应始终记录原因(WHY)

核心模式

将猜测时序的写法替换为等待条件:

// ❌ 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
  }
}

完整实现(含领域专用辅助函数 waitForEvent、waitForEventCount、waitForEventMatch)见 @example.ts。

详细模式、实现指南和常见错误见 @references/patterns-and-implementation.md。

实际效果

来自一次调试会话(2025-10-03):

  • 修复了 3 个文件中的 15 个不稳定测试
  • 通过率:60% → 100%
  • 执行时间:加快 40%
  • 不再有竞态条件

限制

该技能不适用于测试真实时序行为(如防抖、节流间隔)。如果必须使用任意超时,应记录原因。关于该技能的具体安装方式、支持的平台与运行环境,资料中暂未提供。