bug-reproduction について
bug-reproductionは、曖昧な報告を最小限で決定論的な再現手順と回帰テストに変える、開発・QA向けスキルです。git bisectによる導入コミットの特定や、修正を戻してテストの有効性を確認する手順も扱います。
主な機能
- 01報告から再現条件を抽出
- 02再現手順の縮小と切り分け
- 03git bisectによる導入コミット特定
- 04時刻・乱数・ネットワークの固定
- 05修正前に失敗する回帰テストを作成
- 06修正を戻してテストを検証
活用シーン
- 01曖昧なバグ報告を再現可能なケースにする
- 02回帰を引き起こした変更を特定
- 03時刻やネットワークによる不安定な再現を安定化
- 04修正がバグを解消したことを確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ petrkindlmann/ qa-skills --skill bug-reproductionインストール後、Claude Code で/bug-reproductionを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/bisect.mddeterminism.mdintake.mdticket-writeback.md詳細
クイックガイド
| 状況 | 参照先 |
|---|---|
| 情報の少ない報告があり、再現方法がまったくわからない | ステップ 1:暗黙の再現手順を引き出す |
| 14 ステップもある雑然とした再現手順を整理したい | ステップ 2:再現・最小化・切り分け・記録のループ |
| 「先月は動いていたのに、今は壊れている」 | ステップ 3:原因となったコミットまで二分探索する |
| 再現したりしなかったりする | ステップ 4:再現を決定論的にする |
| きれいな再現手順はあるが、まだ修正されていない | ステップ 5:まず失敗する回帰テスト(red)を書く |
| 「開発者は修正済みと言っているのに、テストは成功する」 | ステップ 6:修正が実際に問題を解決しているか検証する |
| 「自分の環境では再現しないが、ユーザーの環境では再現する」 | ステップ 7:不安定・環境依存・再現不能を見分ける |
| 再現手順とテストができている | ステップ 8:証拠をチケットに記録する |
調査時の質問
まず、プロジェクトのルートにある .agents/qa-project-context.md を確認してください。技術スタック、テストランナー、不安定なことがわかっている領域、環境マトリックスが記載されています。すでに回答がある質問は飛ばしてください。ファイルがない場合は、qa-project-context スキルを使って作成するよう提案してください。
- 報告の原文は何ですか? 情報が少ないほど、コードに手を加える前に引き出すべき情報が増えます。1 行だけの報告で、最初の聞き取り全体の方針が決まります(ステップ 1)。
- すでに再現できますか? また、その確実性はどの程度ですか? 「毎回」と「ときどき」のどちらかによって、すぐに最小化へ進むか、まず決定論性の確保に取り組むかが決まります。
- 以前は動作していましたか? 正常に動作していた既知の過去のリリースがあれば、原因となったコミットを特定するために
git bisectを使えます。正常だった時点がわからない場合は、現状から順にデバッグします。 - テストランナーと技術スタックは何ですか? Vitest/Jest と Playwright では決定論性を確保する API(
vi.setSystemTimeとpage.clock)や、回帰テストを追加する場所が異なります。 - 決定論を損なう要因は何ですか? 時刻に依存するルール、乱数、サードパーティ API、ロケールなどは、再現手順を信頼できるものにするため、それぞれ固定する必要があります。
基本原則
-
まず再現し、原因を推測するのはその後です。 最もよくある危険な直感は、症状を読んで根本原因に飛びつくことです(「浮動小数点の丸め誤差っぽいので、合計の計算を修正しよう」など)。そうしてはいけません。再現手順を抽出し、意図したとおりに失敗する状態を作ってから、初めて仮説を立てます。再現手順のない修正は、反証できない推測です。
-
再現手順は物語ではなく、決定論的な成果物です。 「ランダムなコードを使うと、真夜中あたりに発生します」は物語です。時計を固定し、乱数生成器のシードを設定し、ネットワークをスタブ化して、どの実行環境でも同じ入力から同じ失敗が発生するようにします。決定論的でなければ、二分探索もテストも、修正されたことの証明もできません。
-
一度に1つの変数だけを最小化し、変更するたびに再確認します。 再現手順を縮小するのは探索であって、書き換えではありません。手順、データ項目、または依存関係を1つ取り除き、その後、再実行して引き続き再現することを確認します。複数を一度に取り除いても、どれが重要だったのか分かりません。
-
回帰テストは修正前にREDの状態で作成します。 実際に期待する値をアサートし、まずテストが失敗することを確認します(これにより、このバグを検出できると証明できます)。その後、修正によってテストが成功することを確認します。修正後に追加したテストや、無効化/保留中にしたテストでは何も証明できません。
-
テストが成功しても完了ではありません。修正を戻して検証して初めて完了です。 テストが成功しても、間違った理由で成功している可能性があります。修正を一時的に取り除き、テストが再び失敗することを確認します。修正がなければ失敗するテストだけが、バグを確実に防ぎます。
ステップ1:明示されていない再現手順を抽出する
「チェックアウトが壊れていて、注文の合計がときどき間違っています」のような簡潔な報告が示すのは症状であって、発生までの手順ではありません。コードに手を加える前に、再現性に関わるすべての条件を抽出するか、報告者に確認します。想像で再現手順を作ってはいけません。また、根本原因を推測してはいけません。どちらも、バグが再現した後に行うことです。
「合計が間違っている」というデータ正確性のバグでは、簡潔な報告で最もよく抜け落ちている、結果を左右する条件は次のとおりです。
- 正確な再現手順 — クリックごとの操作手順。「チェックアウトが壊れている」ではありません。
- ビルド/バージョン/コミット(git SHA) — すでに修正済みのビルドを使っている可能性があります。
- 環境 — ブラウザーとバージョン、OS、デバイス。
- 入力データ — カートの内容、数量、アカウント/ユーザー、クーポン、正確なフィクスチャ。合計は入力値のみによって決まる関数です。入力値がなければ推測するしかありません。
- 期待値と実際の値 — 期待していた数値と、実際に見た数値。
- 発生頻度 — 毎回発生するのか、それとも断続的に発生するのか。「ときどき」は非決定性を示唆します。
- ロケール/タイムゾーン/通貨 — 丸め、税金、書式設定はロケールによって異なります。
de-DEで「間違っている」合計が、en-USでは正しい場合があります。 - 発生時のタイムスタンプと、関連するログ/スクリーンショット/ネットワークトレース。
コードに手を加える前に、これらを1つの再現仕様にまとめます。空欄があれば、それは報告者に次に尋ねるべき質問です。修正を書き始めたり、推測したりしてよいという意味ではありません。
完全な抽出チェックリスト、「合計が間違っている」場合に各条件が重要な理由、および再現仕様のテンプレートについては、references/intake.mdを参照してください。
ステップ 2:再現→最小化→切り分け→記録のループ
再現することは確認済みでも雑然とした再現手順(例:3 ページにまたがる 14 個の手動 UI 操作)がある場合、開発者にその 14 ステップ版を渡してはいけません。また、全面的に書き直してもいけません。次のループを実行します。
- 再現/確認。 まずベースラインを確立します。完全な再現手順を実行し、実際に失敗することを確認します。現在再現できているものだけを最小化できます。
- 最小化。 ステップ、フィールド、依存関係を1 つ取り除きます。もう一度実行します。それでも再現する場合は、その削除を維持します。失敗しなくなった場合は、その要素が不可欠だったということなので、元に戻します。残った要素がすべて必要になるまで、一度に 1 つの変数だけを変更して繰り返します。これが順序に関する基本ルールです。再現を確認する前に最小化してはいけません。また、削除のたびに失敗が続いていることを確認し忘れてはいけません。
- 切り分け。 失敗が依然として確認できる最小のレイヤーまで絞り込みます。3 ページの UI フローから 1 ページに、さらに UI より下の層にバグがある場合は、問題の関数に対する単体テスト/API 呼び出しまで絞り込みます。
- 記録。 最小化された再現手順を証拠として記録します。最小限のステップまたは単一のコマンドに、ログ/トレース/スクリーンショットを添えます。開発者と回帰テストが使用するのはこれです。
出力するのは、元の手順ではなく、再現に必要な最小限の手順です。
ステップ 3:問題を導入したコミットを二分探索する
バグは HEAD にありますが、過去のリリースでは問題がなく、バグが存在するとゼロ以外で終了するコマンドがあります。git bisect run を使って履歴を自動で二分探索します。各コミットを手動でチェックアウトしてはいけません。また、原因を探すために git revert を使ってもいけません。
git bisect start
git bisect bad HEAD # current commit has the bug (alias: git bisect new)
git bisect good v2.4.0 # last clean release (alias: git bisect old)
git bisect run npm test -- checkout-total.spec.ts # ONE targeted test, never the full suite
# bisect prints "<sha> is the first bad commit"
git bisect reset # ALWAYS clean up — restores the original HEAD
git bisect run が使用する終了コードの規則:終了コード 0 = 良好(バグがない)、ゼロ以外(1~124)= 不良(バグがある)、終了コード 125 = スキップ(テスト不能)です。そのため、機能が正常な場合はコマンドが 0 を返し、バグが再現する場合はゼロ以外を返す必要があります。ほとんどのテストランナーはすでにこの動作をします。npm test:all やテストスイート全体ではなく、対象を絞ったテストを 1 つ実行してください。古いコミットで無関係な失敗があると、そのコミットが不良と判定され、探索が誤った側に進んでしまいます。
good/bad の組み合わせは、回帰(過去は良好で、現在は不良)の場合を想定しています。old/new のエイリアスも同じ探索を意味し、あらゆる状態遷移を調べる場合はこちらのほうが分かりやすい名前です。
一連の手順と、スキップ/テスト不能時のラッパーについては、references/bisect.md を参照してください。
Bisect のスキップと再現性(テスト不能または不安定なコミット)
単純な bisect を誤らせる要因は 2 つあります。また、デフォルトの不良判定である「失敗したら終了コード 1」は、どちらの問題も引き起こします。
- 古いコミットはビルドできない場合があります。 コンパイルエラーは終了コード 1 を返します。bisect はこれを「バグあり」と解釈し、問題のないコミットを不良と判定します。これは誤りです。ビルドできないコミットはテスト不能なので、ラッパースクリプトではビルド失敗時に
exit 125(スキップ)を実行し、実際に不良なコミットと区別する必要があります。 - ネットワーク/タイミングに起因する不安定な失敗。 スタブ化されていないサードパーティへの呼び出しが一時的に失敗すると、終了コード 1 が返り、そのコミットが原因だと判断されます。bisect の実行中はネットワークをスタブ化し、
TZを固定し、RNG にシード値を設定するなどして、結果が再現可能な状態にしてください。これにより、実際のバグだけがそのステップで失敗します。コミットの結果が実行ごとに変わる場合は、不良ではなくテスト不能(exit 125)として扱います。
ステップをスクリプトでラップし、0 = 良好、1 = 不良、125 = スキップ(有効な不良範囲は 1~127 のうち 125 を除く)を返すようにします。ビルドをチェックし、ネットワークをスタブ化し、不安定な動作を検出できるよう 1 回再試行します。その後、git bisect run ./bisect-step.sh を実行します。ラッパーの全体は references/bisect.md を参照してください。
Step 4: 再現を決定論的にする
「真夜中ごろにしか発生せず、ランダムな割引コードを使い、サードパーティの価格設定 API を経由する」というバグには、非決定性の要因が3つあります。3つすべてを固定して、毎回まったく同じように失敗するようにしてください。真夜中を待ったり、実際の価格設定 API にアクセスさせたりしないでください。また、タイミングをごまかすために sleep/setTimeout/waitForTimeout を使わないでください。
| 要因 | Vitest | Playwright |
|---|---|---|
| 時刻 | vi.useFakeTimers() + vi.setSystemTime(new Date('…')) | page.clock.install({ time }) + page.clock.setFixedTime(…) |
| 乱数 | faker.seed(1337)(または Math.random をスタブ化) | init フックでアプリの RNG にシードを設定 |
| ネットワーク | MSW setupServer + http.get → HttpResponse.json | page.route(...) → route.fulfill(...) |
重要なポイント:
- Vitest:
vi.setSystemTimeは、vi.useFakeTimers()の実行後にのみ機能します。beforeEachで faker にシードを設定してください。MSW のonUnhandledRequest: 'error'を設定し、スタブされていないリクエストを大きなエラーとして検出してください。 - Playwright:
page.clock.install/setFixedTimeは、page.gotoより前に実行する必要があります。page.clockはサポート対象の API です。利用できるので、手動でDateを上書きする方法に切り替えたり、テストを「通す」ためにタイムアウトを延ばしたりしないでください。 - バグがロケールに依存する場合は、ロケール/タイムゾーン/通貨(
TZ=UTC、LANG)を固定してください。
避けること: jest.useFakeTimers('legacy')(および timers: 'legacy')。レガシーのフェイクタイマーは非推奨であり、Date/Date.now をモックしないため、時計は動き続け、「固定」したはずの再現でも時刻がずれていきます。Jest 27 以降はモダンタイマーがデフォルトなので、jest.useFakeTimers() + jest.setSystemTime() を呼び出すか、Vitest では vi.useFakeTimers() を呼び出してください。(Jest 30、2025年)
Vitest(vi.useFakeTimers + vi.setSystemTime + faker.seed + MSW setupServer)と Playwright(page.clock + page.route)の完全な手順、および10回実行する決定性チェックについては、references/determinism.md を参照してください。
Step 5: まず失敗する回帰テスト(レッド)を書く
決定論的に再現できる状態になっていて、開発者はまだバグを修正していません。バグに対する TDD として、修正前の今、回帰テストを書いてください。
- 最小限の再現手順を、実際に期待される値を検証するテストとして記述します。
expect(total).toBe(2754)のようにします。常に成功する同語反復的なアサーションでは何も証明できません。 - テストを実行し、修正前に失敗することを確認します。まずレッドにならなければなりません。レッドにならないテストでは、バグを検証できていません。
- テストをコミットする(または修正ブランチにステージする)ことで、CI でテストが修正を保護するようにします。
- 開発者の修正が反映されたら、もう一度実行します。グリーンに変わるはずです。同じテストを使い、編集はしません。
期待される状態:修正前はレッド、修正後はグリーン。
本来の目的を損なう誤った方法——このバグが修正されるまでは実際に失敗するテストに対して、修正が反映された後で初めてテストを書くこと、CI をグリーンに保つためだけに失敗するテストを無効にすること(保留マーカー、常に真になる同語反復的なアサーション、またはアサーションの削除など)、先に修正して後からテストを追加すること——は避けてください。アサーションを有効なままにして、レッドにしてください。
アサーションを組み込む決定論的なテスト本体については、references/determinism.md を参照してください。
ステップ 6:修正によって実際に問題が解決することを検証する
開発者が修正をプッシュし、回帰テストがパスするようになりました。テストがパスすることは必要ですが、それだけでは不十分です。テストは無関係な理由でパスすることがあります。テストがパスしただけでクローズしたり、開発者の言葉をそのまま信じたりしないでください。妥当性を確認します。
- 修正を一時的に元に戻し(スタッシュするか、
git stash/修正行をコメントアウトして)、テストを再実行します。修正なしでも失敗することを確認してください。これによって、テストが実際にこのバグを検証している(テストがバグを検出している)こと、つまり修正のおかげでパスしており、欠陥とは無関係な別の理由でパスしているわけではないことを証明できます。 - 修正を元に戻し、再びパスすることを確認します。
- 複数回、決定論的に再実行し(
--repeat-each/ループを使用)、たまたまパスしたのではなく、安定してパスすることを確認します。
テストが修正なしでは失敗し、修正ありでは繰り返しパスする場合にのみ、修正が検証されたことになります。この修正を元に戻して検証する手順こそが回帰テストの目的であり、最も頻繁に省略される手順です。
ステップ 7:不安定・環境依存・再現不能の切り分け
2 時間試しても再現できませんが、ユーザーには明らかに発生しています。すぐに「再現できない」としてクローズしないでください。不安定だと決めつけて隔離したり、「自分の環境では再現しないから実際には起きていない」と結論づけたりしないでください。これらは、それぞれ異なる証拠に基づく 3 つの別個の診断です。
| 診断 | 判別の証拠 | 対応 |
|---|---|---|
| 不安定 | 同じコード、同じ環境で、同じコミットに対してパスすることも失敗することもある — 1 つの環境で--repeat-each 50(または同じコマンドを何度も再実行)を行い、結果が変わるか確認します | 非決定性の原因(時間/RNG/ネットワーク/競合)を見つけ、決定論的にします(ステップ 4) |
| 環境依存 | 異なる設定 — タイムゾーン、ロケール、ビューポート、OS、ブラウザーのバージョン、CI とローカルの違い — のときにのみ再現し、その設定内では安定している | ユーザーの環境に合わせます。ユーザーのタイムゾーン/ロケール/OS/ブラウザーを再現してから、条件を絞り込みます |
| データ依存 | ユーザー固有のアカウント/入力を使った場合にのみ再現する | ユーザーのデータ/フィクスチャを入手して再現します。バグはプラットフォームではなく入力に起因します |
| 本当に再現不能 | 環境とデータを一致させ、複数回実行しても、上記のいずれも再現しない | 試したこと(環境、実行回数、データ)と再現しなかった結果を記録します — 何も記録せずにクローズしないでください |
このステップで見落とされがちな試み:判断する前に、報告されたユーザーの環境とデータを一致させることです。ユーザーの設定(タイムゾーン、ロケール、OS、ブラウザーのバージョン)を再現し、その環境を用意してから、もう一度実行します。同じ環境での--repeat-eachは、本当の不安定さを切り分けます。環境間での違いは環境依存を示し、入力への依存はデータ依存を示します。
ステップ 8:証拠をチケットに記録する
再現とコミット済みの回帰テストが完了しました。曖昧な元の報告を、構造化されたブロックに置き換えます。UI の 14 ステップをそのまま貼り付けたり、「再現しました。クローズします」とだけ書いたりしないでください。次の 7 項目をすべて含めます。
- 最小限の手順/再現コマンド — 手順全体ではなく、最短の経路です。
- 環境とビルド/コミット — 正確な SHA とプラットフォームです。
- 期待値と実際の値 — 具体的な数値です。
- 導入したコミット —
git bisectで特定した問題のコミットです。 - 回帰テスト — コミット済みテストのファイルとパスへのリンクです。
- 証拠 — ログ、スクリーンショット、トレース、または成果物です。
- 決定性に関するメモ — 誰でも同じように再実行できるように、シード、固定した時刻、スタブを記載します。
コピーして使える Markdown の構成については、references/ticket-writeback.mdを参照してください。
アンチパターン
1. 再現前に修正に取りかかる
「合計が間違っている」と読んで合計計算を修正したり、「丸め誤差のようだ」と推測したりすることです。再現させて失敗させる前に修正してはいけません。再現していないバグに対する修正が機能するとは証明できません。まず再現手順を抽出します(ステップ 1)。
2. 再現することを確認する前に最小化する
実際に失敗することを確認していない再現手順からステップを削ってしまいます。壊れてもいないものを「最小化」することになります。まずベースラインで失敗することを確認してから、削ってください。
3. 複数の変数を一度に削除する
一度に3つのステップを削ると、再現しなくなったときに、どれが影響したのか分かりません。変数を1つずつ削除し、その都度再実行してください。
4. テストスイート全体、または手作業で二分探索する
git bisect run npm test:all を使うと、無関係な失敗によってコミットが不良と判定されることがあります。手動でチェックアウトしてテストする方法は遅く、間違いも起こりやすくなります。git bisect run では対象を絞ったテストを1つ実行してください。
5. 二分探索中にビルドが失敗した場合に exit 1 を返す
ビルドできないコミットを「バグあり」と扱ってしまいます。正常なコミットが不良と判定され、探索結果が台無しになります。テストできないコミットではスキップを示す exit 125 を返し、本当のバグに対してのみゼロ以外の値を返してください。
6. 再現手順で実時間、乱数、またはネットワークを使う
new Date() や Math.random をモックせず、実際の価格設定 API にアクセスしたり、スリープで「深夜まで待ったり」しています。再現結果が運任せになります。時刻を固定し、乱数のシードを設定し、ネットワークをスタブ化してください(ステップ4)。
7. 修正後にテストを書く、またはテストを無効化する
バグがすでに解消された後で回帰テストを追加したり、CI を通すために失敗するテストを無効化したりしています(保留マーカー、恒真なアサーション、アサーションの削除など)。テストでバグを検出できることを証明できません。修正する前に、失敗するテストを書いてください。
8. 元に戻して確認せず、テストが成功しただけで完了とする
「テストが通ったので完了です」。テストは誤った理由で通ることがあります。修正を元に戻して再び失敗することを確認し、修正を戻して再び成功することを確認してください。
9. 最初の再現失敗で「再現できず」として終了する
不安定なケース、環境固有のケース、再現できないケースをひとまとめにして却下しています。判断する前に、ユーザーの環境とデータを一致させ、--repeat-each を使ってください(ステップ7)。
失敗パターン
| 症状 | 考えられる原因 | 修正/確認 |
|---|---|---|
| 二分探索で明らかに無関係なコミットにたどり着く | テストスイート全体、または不安定なテストの失敗によってコミットが不良と判定されている | 対象を絞ったテストを1つに切り替え、exit 125 でスキップし、ネットワークをスタブ化する |
| ローカルでは再現手順が成功するが、CI では失敗する(またはその逆) | 環境固有(TZ、ロケール、OS、ブラウザー) | TZ/LANG を固定し、失敗する環境に合わせる(ステップ7) |
| テストは成功するが、バグを検出できるか確信が持てない | テストを失敗する状態で実行していない | 修正を元に戻して失敗することを確認する(ステップ6) |
vi.setSystemTime が反映されない | vi.useFakeTimers() の前に呼び出している | 先に useFakeTimers() を呼び出す |
page.clock の時刻がアプリの起動時に適用されない | page.goto の後に install/setFixedTime を実行している | ページ遷移の前にクロックを設定する |
| 実行するたびに再現結果が成功と失敗で入れ替わる | 実時間/乱数/ネットワークが固定されていない | ステップ4を適用し、--repeat-each 10 で確認する |
検証
- 再現コマンド/テストは任意に失敗させられます。10回実行し(
--repeat-each 10またはループ)、修正前に毎回失敗することを確認してください。 git bisect run …が「<sha>が最初の不良コミットです」で終了し、git bisect resetによって元のHEADに戻ります。- 修正を元に戻した状態では回帰テストがゼロ以外の値で終了し、修正を適用した状態では 0 で終了します。
- チケットのブロックに、書き戻すべき7つの要素(ステップ8)がすべて含まれています。コミット SHA、テストのパス、決定性に関する注記を検索して確認してください。
完了条件
- 文書化された最小限の再現手順があり、最小の手順または単一のコマンドでオンデマンドに失敗し、複数回の実行でも失敗することが確認されています。
- バグがリグレッションの場合、
git bisectで原因となったコミット SHA が特定され、チケットに記録されています。 - 再現は決定論的です。時間を固定し、RNG にシードを設定し、ネットワークをスタブ化したうえで、同一の実行が10回連続で行われることを確認しています。
- リグレッションテストがコミットされており、修正前は失敗し、修正後は成功し、修正をリバートすると失敗することも確認されています。
- チケットには、7つの要素(最小手順、環境/ビルド、期待値と実際の結果、原因となったコミット、リグレッションテストへのリンク、証拠アーティファクト、決定性に関するメモ)をすべて含む構造化された証拠ブロックがあり、元の曖昧な報告は残さず置き換えられています。
- 再現できなかった場合は、そこに至った証拠とともに、分類(不安定/環境固有/データ依存/再現不能)されています。黙ってクローズされることはありません。
関連スキル
ai-bug-triage— 既存の不具合を分類し、重複を排除して、重大度に応じて振り分けます。トリアージでは、不具合が重要かどうか、どこに影響するかを判断します。不具合を実際に再現して失敗するテストを書くには、こちらを利用してください。トリアージから引き継ぎ、bug-reproduction で対応します。ai-test-generation— 仕様書/PRD/ストーリーからテストを生成します。元の情報が要件の場合に利用してください。元の情報が不具合で、まずそのバグに対してテストを失敗させる必要がある場合は、このスキルを利用してください。test-reliability— 不安定なテストに対する実行時の自己修復と隔離を行います。Step 7 で真の不安定さと診断された場合は、安定化または隔離のためにこちらを利用してください。ここでは診断のみを行います。qa-project-context— スタック、テストランナー、環境マトリックス、既知の不安定な領域など、上記のすべての手順に影響する情報を確認できます。まずこちらを確認してください。- systematic-debugging (
superpowers:systematic-debugging) — 決定論的な再現を確認した後に行う、一般的な根本原因のデバッグループです。このスキルでは、再現と、最終的な修正を保護する失敗するテストを作成します。