基于条件的等待Skill Condition-BasedWaiting

此技能用于软件测试领域,通过条件轮询替代随机超时,提高异步测试的可靠性和稳定性。核心应用包括避免竞争条件、时序依赖和不稳定测试行为,关键词包括异步测试、条件轮询、测试可靠性、竞争条件、时序优化和软件测试自动化。

测试 0 次安装 0 次浏览 更新于 3/17/2026

name: 基于条件的等待 description: 用条件轮询替换随机超时,以实现可靠的异步测试 when_to_use: 当测试存在竞争条件、时序依赖或通过/失败行为不一致时 version: 1.1.0 languages: 所有 progressive_disclosure: entry_point: summary: “用条件轮询替换随机超时,以实现可靠的异步测试” when_to_use: “使用setTimeout/sleep、不稳定测试或依赖时序的异步操作的测试” quick_start: | 1. 识别测试中的随机延迟(setTimeout、sleep、time.sleep()) 2. 替换为基于条件的等待(waitFor模式) 3. 针对常见场景使用领域特定助手 请参阅@example.ts获取完整的工作实现 core_pattern: | // ❌ 猜测时序 await new Promise(r => setTimeout(r, 50));

  // ✅ 等待条件
  await waitFor(() => getResult() !== undefined);

references: - path: references/patterns-and-implementation.md purpose: 详细的等待模式、实现指南和常见错误 when_to_read: 实现waitFor或调试时序问题时

基于条件的等待

概述

不稳定的测试通常通过随机延迟来猜测时序。这会创建竞争条件,导致测试在快速机器上通过,但在负载下或CI中失败。

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

何时使用

digraph when_to_use {
    "测试使用setTimeout/sleep吗?" [shape=diamond];
    "测试时序行为吗?" [shape=diamond];
    "记录为什么需要超时" [shape=box];
    "使用基于条件的等待" [shape=box];

    "测试使用setTimeout/sleep吗?" -> "测试时序行为吗?" [label="是"];
    "测试时序行为吗?" -> "记录为什么需要超时" [label="是"];
    "测试时序行为吗?" -> "使用基于条件的等待" [label="否"];
}

使用时机:

  • 测试中存在随机延迟(setTimeoutsleeptime.sleep()
  • 测试不稳定(有时通过,负载下失败)
  • 并行运行时测试超时
  • 等待异步操作完成

不使用时:

  • 测试实际时序行为(防抖、节流间隔)
  • 如果使用随机超时,请始终记录原因

核心模式

// ❌ 之前:猜测时序
await new Promise(r => setTimeout(r, 50));
const result = getResult();
expect(result).toBeDefined();

// ✅ 之后:等待条件
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)); // 每10毫秒轮询一次
  }
}

请参阅@example.ts获取包含领域特定助手(waitForEventwaitForEventCountwaitForEventMatch)的完整实现。

有关详细模式、实现指南和常见错误,请参阅@references/patterns-and-implementation.md

实际影响

从调试会话(2025-10-03):

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