test-reliability について
test-reliability は、不安定なテストの原因を特定し、自動修復をレビュー可能にするチーム向けのスキルです。原因分類、堅牢なロケーター、環境やテストデータの修復、隔離、信頼度付きの修復証拠を扱います。
主な機能
- 01フレーク原因の分類
- 02複数属性による堅牢なロケーター
- 03環境を考慮した失敗診断
- 04古いテストデータの復旧
- 05信頼度付き修復ワークフロー
- 06テスト隔離の管理指針
活用シーン
- 01断続的な CI 失敗のトリアージ
- 02レビュー候補を使った壊れたセレクターの置き換え
- 03バックエンドやランナーの問題と UI 障害の切り分け
- 04トークン期限切れやレコード欠落からのテスト復旧
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ petrkindlmann/ qa-skills --skill test-reliabilityインストール後、Claude Code で/test-reliabilityを入力して読み込まれたか確認します。
同梱ファイルreferences/flaky-test-runbook.md
詳細
クイックガイド
| 状況 | 移動先 |
|---|---|
| CIでテストが不安定だが、根本原因が不明 | 不安定なテストの分類 → 判断ツリー |
| ロケーターが壊れたため、堅牢な代替を探している | ロケーターの堅牢性 |
| アクションが失敗するが、UIの不具合ではなくバックエンドの遅延が疑われる | 環境を考慮した修復 |
| テスト中に古いデータが原因で401/404/429が発生する | テストデータの修復 |
| レビューゲート付きの自動修復を利用したい | 可視化された修復ワークフロー |
| CIをブロックせずに不安定なテストを隔離したい | 隔離の管理 |
| 不安定なテストを1つずつ段階的にトリアージする | references/flaky-test-runbook.md |
調査の質問
最初に .agents/qa-project-context.md を確認してください。既知の不安定な箇所、セレクター戦略、CI環境の詳細が記載されています。すでに答えが記載されている質問は省略してください。
- 現在の不安定なテストの割合はどのくらいですか? 過去30日間のCI失敗統計を確認してください。2%未満なら健全、2~5%なら対処が必要、5%を超えるとチームの信頼が損なわれます。
- 問題はどこに集中していますか? ロケーターの破損、タイミング、テストデータ、環境のどれでしょうか? 不明な場合は、まず計測してください(「不安定なテストの分類」を参照)。
- 現在のセレクター戦略は何ですか? どこでも
data-testidを使う、CSSとロールベースを併用する、あるいは決まった戦略がなくその場で機能するものを使う、のどれでしょうか? これを基に安定性スコアの基準値を設定します。 - 現在、不安定なテストにどう対処していますか? 再試行して成り行きに任せる、スキップして忘れる、あるいは何らかの体系的な方法を使う、のどれでしょうか? 必要なプロセスの追加量を判断します。
- テストを実行するCI環境はどのようなものですか? 毎回同じマシンで実行するのか、それとも異なるランナーで実行するのか。リソースは一定か、変動するのか。環境の問題かテストの問題かを見分ける際の手がかりになります。
- テストデータ戦略はどのようなものですか? 共有データベース、テストごとのフィクスチャー、ファクトリーによるシード、外部サービスのどれでしょうか? テストデータの修復が適用できるかどうかを判断します。
基本原則
-
修正より予防を優先します。 堅牢なテストを書くコストは1倍です。不安定なテストの調査には10倍かかります。テストスイートに対するチームの信頼を失うコストは100倍です。
-
修復は可視化され、レビュー可能でなければなりません。 自動修復のたびに、何が壊れ、何を試し、何がうまくいったか、信頼度スコアはいくつかを示す証拠を生成します。修正を黙って行うと、失敗を黙って放置するのと同じ速さで信頼が損なわれます。
-
修正の前に分類します。 タイミングの問題への対処と、データ依存の問題への対処はまったく異なります。診断を誤ると、労力が無駄になるだけでなく、状況を悪化させることもあります。
-
不安定なテストはバグです。 我慢すべき厄介事ではありません。不安定なテストは、テストのバグ(テストを修正する)、アプリのバグ(アプリを修正する)、または環境の問題(環境を修正する)のいずれかを示しています。
-
信頼性は感覚ではなく指標として追跡します。 不安定なテストの割合、修復までの平均時間、隔離期間、セレクターの安定性を測定します。測定すれば修正につながります。
-
自己修復には段階があります。 まず堅牢なロケーター(レベル1)から始め、フォールバック戦略(レベル2)、環境を考慮した修復(レベル3)、信頼度スコア付きの自動修復(レベル4)と進めます。レベル1を習得する前にレベル4へ進まないでください。
ロケーターの堅牢性
複数属性セレクター(フォールバックチェーンを超えて)
単一のロケーター戦略は、単一障害点になります。複数属性セレクターは、1つの要素を検索する際に複数のシグナルを組み合わせ、フォールバックチェーンを複雑にせずに堅牢性を高めます。
重要な考え方: 「Aを試し、次にB、次にC」ではなく、「A、B、Cに一致する要素を探し、シグナルが1つ欠けても許容する」ということです。
// Multi-attribute locator: tries combinations from most specific to least
const submitBtn = await multiAttributeLocator(page, {
testId: 'checkout-submit', // most stable signal
role: 'button', // semantic signal
name: /place order/i, // accessible name
nearText: 'Order Summary', // visual context
});
// Internally: tries testId+role+name first, then testId alone, then role+name,
// then text, then nearText+role. Returns first visible match.
// Unlike fallback chains, it combines signals for higher confidence.
DOM の類似性/隣接コンテキストのマッチング
ロケーターが失敗しても、属性が変更されただけで要素は存在している可能性があります。周囲の DOM コンテキストを使って見つけます。
- 親要素 + タグ + type: コンテナー(testId で)を見つけ、その中でタグと type を使って特定します。
- 直前のラベル: 兄弟要素のテキスト(ラベル)を見つけ、その隣にある入力要素/ボタンを特定します。
- 近くのテキストコンテキスト: 対象の近くにある表示テキストを見つけ、同じ親要素内で要素の種類を使って特定します。
これらは以下の信頼度システムでスコア付けされる修復候補であり、実行時のフォールバックではありません。
セレクターの安定性スコア
リファクタリングの優先順位を決めるため、すべてのセレクターを 0~5 のスケールで評価します。
| スコア | 方法 | 維持される変更 |
|---|---|---|
| 5 | getByTestId('submit-order') | CSS、テキスト、構造の変更 |
| 4 | getByRole('button', { name: 'Submit' }) | CSS、構造の変更 |
| 3 | getByLabel('Email') | CSS の変更。ラベルの文言変更で壊れます |
| 2 | getByText('Submit Order') | 文言の変更で壊れます |
| 1 | locator('.btn-primary.submit') | CSS または構造の変更で壊れます |
| 0 | locator('//div[3]/button[1]') | DOM の変更で壊れます |
目標: スイート全体の平均スコアを 3.5 以上にします。毎月監査します。スコア 0 とスコア 1 のセレクターの修正を優先します。ロケーターごとに 1 つのスコアを selector-stability.md(または CI のステップ)に出力し、スイートの平均を報告して、3.5 の目標を単なる主張ではなく検証可能にします。
フレークの分類フレームワーク
すべての不安定なテストには根本原因のカテゴリがあります。正しく分類することで、修正方法が決まります。
カテゴリ
| カテゴリ | 兆候 | 根本原因 | 修正方針 |
|---|---|---|---|
| タイミング | タイムアウトエラー、再試行で成功、CI で悪化 | 競合状態、アニメーション、非同期処理 | 時間ではなく条件を待つ |
| データ依存 | 他のテストと同時に実行すると失敗し、単独では成功 | 共有状態、クリーンアップ不足 | テストごとに分離し、フィクスチャをクリーンアップ |
| 環境 | 特定のランナーで失敗し、負荷と相関する | リソース競合、ネットワーク遅延 | 外部サービスをモックし、リソースを増やす |
| 実行順依存 | --shard または fullyParallel で失敗 | 別のテストの副作用に依存 | 自己完結型のセットアップ |
| 時間依存 | 特定の時刻(日付変更時、月末)に失敗 | 実際の時計、日付境界を使用 | 時計をモックし、相対比較を使う |
| ビジュアルレンダリング | スクリーンショットの差分が不安定になり、サブピクセルの差異が出る | フォントレンダリング、アンチエイリアス、アニメーションフレーム | しきい値を上げ、動的な領域をマスク |
| 外部サービス | サードパーティの稼働状況と相関する | テスト内で実際の HTTP 呼び出しを実行 | 外部 API をモック |
分類の判断ツリー
Test is flaky
│
├── Does it pass when run alone?
│ ├── YES → ORDER DEPENDENCY or DATA DEPENDENCY
│ │ ├── Does another test create/modify data it needs? → ORDER DEPENDENCY
│ │ └── Does it share a database/file/cache? → DATA DEPENDENCY
│ │
│ └── NO → Not order/data dependent. Continue below.
│
├── Does it fail more often in CI than locally?
│ ├── YES → TIMING or ENVIRONMENT
│ │ ├── Timeout errors? → TIMING (CI is slower)
│ │ ├── Connection errors? → ENVIRONMENT (network latency / service)
│ │ └── Resource errors (OOM, disk)? → ENVIRONMENT (resource contention)
│ │
│ └── NO → Same rate locally and CI. Continue below.
│
├── Does it fail at specific times?
│ ├── YES → TIME SENSITIVITY
│ │ ├── Near midnight? → Date boundary issue
│ │ ├── Near month/year end? → Calendar calculation
│ │ └── Specific hour? → Timezone issue
│ │
│ └── NO → Continue below.
│
├── Does it involve screenshots or visual comparison?
│ ├── YES → VISUAL RENDERING
│ │
│ └── NO → Continue below.
│
├── Does it call external HTTP APIs?
│ ├── YES → EXTERNAL SERVICE
│ │
│ └── NO → TIMING (most likely — default classification)
│ └── Investigate: what async operation is not being awaited?
各カテゴリの修正方法は、コードパターン全体とともに references/flaky-test-runbook.md のステップ 4 にあります。
環境を考慮した修復
テストの失敗がすべてテストの問題とは限りません。環境の問題である場合もあります。環境を考慮した修復では、この 2 つを区別して対応します。
バックエンドの低速と実際の UI の失敗
操作が失敗した場合は、テストを原因と決めつける前にバックエンドの状態を確認します。
- 操作が失敗 →
/api/healthにアクセスします。 - バックエンドが異常(5xx またはタイムアウト) → 指数バックオフで再試行します。
backend_downとして診断します。これは UI のバグではありません。 - バックエンドが正常(2xx) → これは実際の UI/テストの失敗です。再試行しないでください。
構造化された診断結果を返します:{ success: boolean; diagnosis: 'backend_down' | 'ui_failure' | 'backend_slow_recovered' }。これはフレークの分類に使われます。バックエンドの問題は環境の問題であり、テストのバグではありません。
リソース競合の検出
CI でテストの失敗と判断する前に、リソース競合がないか確認します。
- ブラウザーの健全性:
about:blankを読み込みます。2 秒を超える場合(ベースラインは 500ms 未満)、ランナーに過負荷がかかっています。 - API の健全性:
/api/healthにアクセスします。5 秒を超える場合(ベースラインは 1 秒未満)、バックエンドに負荷がかかっています。 - 診断: いずれかの確認に失敗した場合は環境の問題として分類し、テスト結果に注記を付けます。リソース競合による失敗は、不安定なテストの割合に含めないでください。
データ修復
テストデータは期限切れになったり、クリーンアップされたり、無効になったりします。データ修復では、古くなったテストデータを検出して再生成します。
よくあるデータ障害パターン
| パターン | シグナル | 修正方法 |
|---|---|---|
| 認証トークンの期限切れ | テスト中の401レスポンス | フィクスチャでトークンを再生成する |
| テストレコードの削除 | シードデータへのアクセス時に404 | テスト前に再シードする |
| 一意性制約違反 | 409または制約エラー | 実行ごとに一意の識別子を生成する |
| 古いキャッシュ | 誤ったデータが返される | セットアップでキャッシュをクリアする |
| クォータ超過 | 429またはレート制限エラー | クォータをリセットするか、専用のテストアカウントを使用する |
自己修復テストデータのフィクスチャパターン
データの存在を確認し、古くなっている場合は再生成するフィクスチャを作成します。
// Pattern: verify → heal → use → cleanup
testUser: async ({ request }, use, testInfo) => {
// 1. Try to find existing test user by deterministic email
// 2. Verify auth token is still valid (GET /api/me)
// 3. If token expired → refresh it (POST /refresh-token), mark as healed
// 4. If user missing → create new one, mark as healed
// 5. If healed → annotate testInfo for observability
// 6. use(user) → run the test
// 7. Cleanup: delete test user (guaranteed by fixture, even on failure)
}
主なパターン:
- テストごとの一意性を確保するため、メールアドレスや識別子に
testInfo.testIdを使用します。 - 修復が発生した場合は、可観測性を確保するために
testInfo.annotationsに注釈を付けます。 - クリーンアップは必ずフィクスチャの使用後ブロックで行い、
afterEachでは行わないでください。フィクスチャは失敗時にもクリーンアップを保証します。
可観測な修復ワークフロー
基本的なガードレール: 修復は観測可能で、レビュー可能でなければなりません。すべての修復は次のフローに従います。
Failure Detected
│
▼
Candidate Repair Generated
│
▼
Confidence Score Computed (0.0 - 1.0)
│
▼
Evidence Diff Produced (what changed, what was tried)
│
▼
Approval Policy Applied
│ ├── Score >= 0.9 → Auto-apply, log for batch review
│ ├── Score 0.7-0.89 → Apply in quarantine, flag for individual review
│ ├── Score 0.5-0.69 → Do NOT apply, open PR with evidence for review
│ └── Score < 0.5 → Discard, manual investigation required
│
▼
Intent Fidelity Check (does repaired test still test the same thing?)
│
▼
Rollback if intent fidelity drops
信頼度スコアリング
修復候補を6つの観点で採点します(加重合計、0.0~1.0)。
| 観点 | 重み | スコアリング |
|---|---|---|
| マッチの具体性 | 0.30 | testId=1.0、role=0.9、text=0.7、context=0.5、CSS=0.3 |
| 要素の表示状態 | 0.15 | 表示されていれば1.0、そうでなければ0.0 |
| 同じ親コンテナ | 0.15 | 同じコンテナなら1.0、異なる場合は0.0 |
| 同じ要素タイプ | 0.15 | 同じタグとロールなら1.0、異なる場合は0.0 |
| テキストの類似度 | 0.15 | 0.0~1.0(アクセシブル名のレーベンシュタイン比) |
| 属性の重複 | 0.10 | 0.0~1.0(共有属性のジャッカード係数) |
スコアのしきい値(半開区間——0.9は自動適用の段階にのみ含まれます):
- >= 0.9 — 自動適用し、バッチレビュー用に記録します。
- 0.7-0.89 — 隔離環境で適用し、個別レビューの対象としてフラグを付けます。
- 0.5-0.69 — 適用せず、レビュー用の根拠を添えてPRを作成します。
- < 0.5 — 破棄し、手動での調査を必要とします。
references/flaky-test-runbook.md(スコアの解釈)では、この4つの区分をそのまま使用しています。編集する場合は、3か所すべてで内容を一致させてください。
修復の証拠
修復のたびに、テストファイル、テスト名、障害の種類、元のロケーター、修復候補(それぞれの信頼度、根拠の文字列、意図が保持されているかのフラグを含む)、選択された候補、タイムスタンプ、承認経路、ロールバックのトリガー、**および修復実行を記録したscreencast.webm(showActions注釈付きのPlaywright 1.59+ page.screencastによる「エージェント型の動画証跡」)**を含む証拠レコードを生成します。スクリーニングキャストがなければ、レベル3/4の修復済みテストでは、レビュアーはJSONレコードを信頼する必要があります。スクリーニングキャストがあれば、差分と実行時の状態の両方を確認できます。
購入するか、構築するか
信頼度スコア付きのヒーラーを自社で所有したくない場合は、レベル3〜4をベンダーに任せます。オンプレミス/エアギャップ環境へのデプロイ、明示的なAIプロンプトの監査証跡、またはトラッカーでは表現できない隔離ロジックが必要な場合は、社内で構築します。ホスト型ツールがすでにフィンガープリントの生成、クラスタリング、自動隔離をすべて行ってくれるなら、構築する価値はなくなります。
- Selenium / Selenide / Robot Framework のテストスイートには、専用の自己修復レイヤーとしてHealeniumが引き続き適しています(現在はAWS Marketplaceで提供されています)。
- Playwrightでエージェント主導の自己修復を行う場合、標準的な方法はPlaywright MCPです。CLIとスキルだけでは不十分なときに、エージェントがブラウザーをリアルタイムで操作し、置き換え用のロケーターを見つけられます。
- このワークフローを提供するホスト型プラットフォーム:Trunk Flaky Tests(自動隔離+AIによる失敗のクラスタリング。2026 Quarantined Tests APIは現在の隔離リストをプログラムから取得できます。このスキルで手作業で構築するものと同じテスト衛生ワークフローです)、CloudBees Smart Tests(旧称Launchable。古いドキュメントを検索するエージェントは旧名称を見つけることがあります)、Datadog Test Optimization(2024年12月に「Datadog Test Visibility」から名称変更)。3つともレベル3〜4に対応するフィンガープリント生成、クラスタリング、自動隔離を提供しています。
意図の忠実性の確認
修復を適用した後、テストが引き続き同じユーザーの意図を検証していることを確認します。
- 要素の種類が変わった(例:
button→a)— 意図が維持されていないため、ロールバックします。 - ARIAロールが変わった(例:
button→link)— 意図が維持されていないため、ロールバックします。 - フォームのアクションが変わった(別のエンドポイントを対象にしている)— 意図が維持されていないため、ロールバックします。
- タグ、ロール、フォームのアクションが同じ — 意図が維持されているため、修復を維持します。
テストが検証する内容(要素の見つけ方だけではありません)を変える修復は、ロールバックする必要があります。
隔離の管理
隔離は不安定なテストを切り離し、テスト自体は実行しながらCIをブロックしないようにします。設定全体とCIへの組み込みについては、references/flaky-test-runbook.md(手順6)を参照してください。要点は次のとおりです。
// Tag the flaky test
test('intermittent WebSocket reconnect', {
tag: ['@quarantine'],
annotation: {
type: 'quarantine',
description: 'Flaky since 2026-03-15. Race condition in WebSocket handler. Ticket: BUG-1234.',
},
}, async ({ page }) => { /* ... */ });
// playwright.config.ts — separate projects
projects: [
{ name: 'stable', testMatch: /.*\.spec\.ts/, grep: /^(?!.*@quarantine)/ }, // exclude quarantine
{ name: 'quarantine', grep: /@quarantine/, retries: 3 },
],
CIでは、--project=stableをブロッキングステップとして実行し、--project=quarantineはcontinue-on-error: trueを指定して実行します。これにより、quarantineプロジェクトがパイプラインをブロックすることはありません。
隔離のライフサイクル
1. DETECT — Test identified as flaky (CI reporter or manual triage)
2. TAG — Add @quarantine annotation with ticket link and date
3. ISOLATE — Quarantine project runs separately, does not block
4. DIAGNOSE — Follow the flaky test runbook (references/flaky-test-runbook.md)
5. FIX — Apply the fix pattern for the classified category
6. VERIFY — Run 50x with --repeat-each, zero failures required
7. RELEASE — Remove @quarantine tag, add annotation documenting the fix
隔離の衛生ルール
- 隔離期間の上限は14日です。 14日を過ぎたら修正するか、削除します。恒久的な隔離は恒久的な腐敗につながります。
- 隔離エントリーにはすべてチケットへのリンクを付けます。 匿名の隔離は禁止です。
- 毎週レビューします。 各スプリントで隔離リストを確認します。長期化している隔離はエスカレーションします。
- 隔離の規模を追跡します。 テストの5%を超える数が隔離されている場合、テストを修正するだけでなく、プロセスの変更が必要な全社的な問題を示しています。
アンチパターン
1. セレクターを黙って置き換える
ログもレビューも信頼度スコアもなく、壊れたセレクターを置き換えることです。修復後のテストが、まったく別の要素を検証している可能性があります。修復のたびに証拠を残す必要があります。
2. 「とりあえず再試行」を修正と見なす
再試行は検出手段であり、修正ではありません。3回中2回目の再試行が必要なテストも、最も重要なリリース時にはいずれ3回すべて失敗します。
3. 不安定なテストを恒久的に無効化する
test.skip('flaky, will fix later') — 「後で」は決してやってきません。追跡付きで隔離するか、完全に削除します。チケットのないスキップ済みテストは、使われないコードです。
4. すべての不安定さを同じように扱う
タイミングの問題とデータ依存の問題では、必要な修正がまったく異なります。データ依存の問題にwaitForTimeout(5000)を追加すると、テストは遅くなるうえ、依然として不安定なままです。
5. 安定性の修正としてwaitForTimeoutを使う
// NEVER the right fix
await page.waitForTimeout(5000);
// Wait for the actual condition
await expect(page.getByRole('table')).toBeVisible();
await page.waitForResponse(resp => resp.url().includes('/api/data') && resp.status() === 200);
6. 可観測性なしで修復する
ログ、証拠、信頼度スコアを生成しない自動修復です。測定できないものは改善できず、レビューできないものは信頼できません。
7. 安定したテストを書く前に修復を過剰に設計する
基本的な堅牢なロケーターパターンを採用する前に、複雑な自己修復フレームワークを構築することです。まずは複数属性セレクターと適切な待機処理から始めます。破損の発生箇所をデータで把握してから、修復の基盤を追加します。
8. 隔離期間に期限を設けない
テストが何カ月も隔離されたままになっています。隔離は一時的な状態であり、恒久的な置き場所ではありません。最長14日間に制限してください。
失敗モード
| 症状 | 考えられる原因 | 対処または確認事項 |
|---|---|---|
バックエンドのヘルスチェック自体が不安定になり、backend_down と誤診される | ヘルスエンドポイント自体が不安定、または応答が遅い | ヘルスエンドポイント自体の p99 を個別に追跡し、単一のプローブだけで診断を決めないでください |
| 修復用動画を有効にした後、アーティファクトのストレージ使用量が急増する | 修復時だけでなく、すべての実行で page.screencast の録画を行っている | 正常な処理経路ではなく、修復経路でのみ録画してください |
| 自動修復の精度が80%を下回る | 信頼度のしきい値が低すぎる、または意図との整合性チェックが省略されている | 自動適用の下限を引き上げ、意図のチェックは絶対に省略しないでください |
| 隔離プロジェクトによってパイプラインがブロックされる | 隔離用 CI ステップに continue-on-error が設定されていない | 追加してください。隔離プロジェクトがマージをブロックすることは、決してあってはなりません |
検証
- 不安定な動作を再現します:
npx playwright test <spec> --repeat-each=20 --workers=4 --trace=on— 修正を信頼する前に、少なくとも一度は失敗する必要があります。 - 修正後、安定性を証明します:
npx playwright test <spec> --repeat-each=50 --workers=4— CI の実行条件でも、50回中50回の成功を必須とします。 - 隔離への振り分けを確認します:
npx playwright test --project=stableでは@quarantineテストが除外され、--project=quarantineではそれらのテストのみが実行されます。 - セレクター監査で数値が出力されることを確認します:安定性レポートに、各ロケーターのスコアとスイートの平均値が記載されます。
完了条件
- すべての不安定なテストが特定され、根本原因(タイミング、データ依存、環境など)に基づいて分類されています。
- 各不安定なテストは隔離されるか修正されています。文書化された計画とチケットの参照なしに、テストが暗黙に再試行されることはありません。
selector-stability.md(または CI レポート)に各ロケーターの安定性スコアが記載され、スイートの平均が >= 3.5 と報告されています。- 不安定テスト率のメトリクス(再試行時に成功するテストの割合)が CI ダッシュボードに公開され、チームから確認できる状態になっています。
- 隔離された各テストにチケットの参照先と、14日以内の期限日が設定されています。
関連スキル
- selector-drift-recovery — 計画的な UI リファクタリング後に、多数のセレクターをオフラインで一括再生成する場合に使用します。このスキルは実行時に1つのテストを修復します。
- playwright-automation — ここで紹介するパターンの基盤となる、Playwright のセットアップ、ページオブジェクトモデル、フィクスチャ、CI 連携について説明します。
- ci-cd-integration — 上記で参照したパイプライン設定、並列実行、隔離ジョブの配線について説明します。
- qa-metrics — 不安定テスト率、修復までの平均時間、隔離数、セレクターの安定性を長期的に追跡します。
- ai-bug-triage — 不安定な動作の調査で実際のアプリのバグが見つかった場合は、失敗をトリアージパイプラインに渡して分類・報告します。