用条件轮询替代固定等待,提升异步测试的稳定性与可维护性。
复制安装指令,让 AI 自动完成配置 · 推荐新手
请帮我安装 askskill 上的 "Condition-Based Waiting" 技能: 1. 下载 https://raw.githubusercontent.com/obra/clank/main/skills/testing/condition-based-waiting/SKILL.md 2. 保存为 ~/.claude/skills/condition-based-waiting/SKILL.md 3. 装好后重载技能,告诉我可以用了
请把这个依赖 sleep(3000) 的前端测试改写成基于条件等待:持续检查“提交成功”提示是否出现,设置合理超时与轮询间隔,并在超时时输出清晰错误信息。
一段改写后的测试代码,使用条件轮询等待提示出现,并包含超时处理与可读性更好的失败信息。
我有一个集成测试在触发导出任务后固定等待 10 秒。请改成轮询任务状态接口,直到状态变为 completed 或失败,并给出可复用的等待函数。
包含通用等待辅助函数的测试示例,通过轮询状态接口判断任务完成,而不是依赖固定时长等待。
请帮我重构这个测试:数据写入后不要直接等待 5 秒再查数据库,而是循环检查目标记录是否存在且字段值正确;同时说明这样做为什么更稳定。
重构后的测试逻辑,采用条件检查数据库状态,并附带简要说明其稳定性和减少误报的原因。
Flaky tests often guess at timing with arbitrary delays. This creates race conditions where tests pass on fast machines but fail under load or in CI.
Core principle: Wait for the actual condition you care about, not a guess about how long it takes.
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"];
}
Use when:
setTimeout, sleep, time.sleep())Don't use when:
// ❌ 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();
| Scenario | Pattern |
|---|---|
| Wait for event | waitFor(() => events.find(e => e.type === 'DONE')) |
| Wait for state | waitFor(() => machine.state === 'ready') |
| Wait for count | waitFor(() => items.length >= 5) |
| Wait for file | waitFor(() => fs.existsSync(path)) |
| Complex condition | waitFor(() => obj.ready && obj.value > 10) |
Generic polling function:
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
}
}
See @example.ts for complete implementation with domain-specific helpers (waitForEvent, waitForEventCount, waitForEventMatch) from actual debugging session.
❌ Polling too fast: setTimeout(check, 1) - wastes CPU
✅ Fix: Poll every 10ms
❌ No timeout: Loop forever if condition never met ✅ Fix: Always include timeout with clear error
❌ Stale data: Cache state before loop ✅ Fix: Call getter inside loop for fresh data
// 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
Requirements:
From debugging session (2025-10-03):
先用伪代码梳理方案与迭代思路,再高效转成可执行代码。
帮助开发者用早返回或表驱动方式简化嵌套条件分支,提升代码可读性。
帮助你为变量选择清晰准确、易维护的命名,提升代码可读性。
帮助开发者保持类接口抽象一致,避免混杂序列化、持久化等无关职责。
帮助你撰写不过时的代码注释,聚焦做什么与为什么而非时序背景。
帮助用户检索过往 Claude Code 对话,快速找回事实、决策与上下文线索。