关于 condition-based-waiting
condition-based-waiting 是帮助开发者减少测试时序问题的测试技能。它指导测试等待实际条件而非任意延迟,并说明轮询、超时及确需固定等待时的处理方式。
主要功能
- 01等待状态或事件满足条件
- 02提供常见条件轮询模式
- 03为轮询设置超时和错误信息
- 04说明固定延迟的适用情形
适用场景
- 01替换测试中的任意 sleep 延迟
- 02等待异步操作或文件出现
- 03排查并行运行时不稳定的测试
- 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
要求:
- 首先等待触发条件
- 基于已知的时间安排(而非猜测)
- 添加注释说明原因
实际效果
来自调试会话(2025-10-03):
- 修复了 3 个文件中的 15 个不稳定测试
- 通过率:60% → 100%
- 执行时间:缩短 40%
- 不再出现竞态条件
评论
支持 Markdown