condition-based-waiting について
推測による遅延の代わりに、観測可能な条件を待つテストスキルです。非同期処理、負荷、並列実行に起因する不安定なテストの削減に役立ちます。
主な機能
- 01固定遅延ではなく条件を待機
- 02イベント、状態、件数、ファイルの待機
- 03複合条件を使った待機処理
- 04用途別イベントヘルパーの利用
活用シーン
- 01テスト内の任意の sleep 呼び出しの置き換え
- 02高負荷時に失敗するテストの安定化
- 03非同期処理の完了待ち
- 04並列テスト実行時のタイムアウト回避
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ bobmatnyc/ claude-mpm-skills --skill condition-based-waitingインストール後、Claude Code で/condition-based-waitingを入力して読み込まれたか確認します。
同梱ファイルmetadata.jsonreferences/patterns-and-implementation.md
詳細
条件に基づく待機
概要
不安定なテストでは、根拠のない遅延時間を設定してタイミングを推測しがちです。その結果、処理の速いマシンではテストが成功しても、負荷がかかっているときや 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
}
}
ドメイン固有のヘルパー(waitForEvent、waitForEventCount、waitForEventMatch)を含む完全な実装については、@example.ts を参照してください。
詳細なパターン、実装ガイド、よくある間違いについては、@references/patterns-and-implementation.md を参照してください。
実際の効果
デバッグセッションより(2025-10-03):
- 3 ファイルにわたる不安定なテスト 15 件を修正
- 成功率:60% → 100%
- 実行時間:40% 短縮
- 競合状態が発生しなくなった
コメント
Markdown 対応