mcpField
C
condition-based-waiting

condition-based-waiting

github.com/jackspace测试与质量1637 次安装12 周前更新
代码仓库

关于 condition-based-waiting

condition-based-waiting 是帮助开发者减少测试时序问题的测试技能。它指导测试等待实际条件而非任意延迟,并说明轮询、超时及确需固定等待时的处理方式。

主要功能

  1. 01等待状态或事件满足条件
  2. 02提供常见条件轮询模式
  3. 03为轮询设置超时和错误信息
  4. 04说明固定延迟的适用情形

适用场景

  1. 01替换测试中的任意 sleep 延迟
  2. 02等待异步操作或文件出现
  3. 03排查并行运行时不稳定的测试
  4. 04验证具有已知间隔的定时行为

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jackspace/claudeskillz --skill condition-based-waiting

安装后在 Claude Code 中输入/condition-based-waiting确认已加载。

附带文件example.tsSKILL.json

详细介绍

基于条件的等待

概述

不稳定的测试常会用任意延迟来猜测等待时间。这会造成竞态条件:测试在速度较快的机器上通过,却在负载较高时或 CI 中失败。

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

适用场景

digraph when_to_use {
    "Test uses setTimeout/sleep?" [shape=diamond];
    "Testing timing behavior?" [shape=diamond];
    "Document WHY timeout needed" [shape=box];
    "Use condition-based waiting" [shape=box];

    "Test uses setTimeout/sleep?" -> "Testing timing behavior?" [label="yes"];
    "Testing timing behavior?" -> "Document WHY timeout needed" [label="yes"];
    "Testing timing behavior?" -> "Use condition-based waiting" [label="no"];
}

适用于:

  • 测试中有任意延迟(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
✅ 修复: 每 10 ms 轮询一次

❌ 没有超时: 如果条件始终不满足,循环将永远持续
✅ 修复: 始终设置超时并提供清晰的错误信息

❌ 数据过期: 在循环开始前缓存状态
✅ 修复: 在循环中调用 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

要求:

  1. 首先等待触发条件
  2. 基于已知的时间安排(而非猜测)
  3. 添加注释说明原因

实际效果

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

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

评论

支持 Markdown