legacy-testing について
legacy-testing は、テストのないコードに保護網を作ってからリファクタリングするスキルです。重要な振る舞いと入口を評価し、承認用の段階的な計画を作成した後、実行と報告を支援します。
主な機能
- 01既存テストと未テスト領域の評価
- 02重要な振る舞いと外部入力の入口を特定
- 03承認用のテスト計画を作成
- 04テストと測定の手順を案内
- 05測定後にリファクタリングを配置
活用シーン
- 01レガシーコードの改修前にテストを整備
- 02金額や権限のルールにテストを選定
- 03外部入力から到達できるクラッシュを探す
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dmoka/ skills --skill legacy-testingインストール後、Claude Code で/legacy-testingを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
examples/ticketbay-fees.mdreferences/approval-tests.mdcharacterize.mdmake-it-testable.mdmeasure.mdproperty-testing.mdrefactor.mdscripts/assess.sh詳細
レガシーコードのテスト
レガシーコードとは、テストのないコードです。リファクタリングを始める前に、ビジネスを支える振る舞いの下にセーフティネットを構築します。まず評価し、提案し、承認を待ってから実行します。
手順
- 評価する。 リポジトリのルートから
scripts/assess.sh [path]を実行します。パスにはファイルまたはフォルダーを指定します。このスクリプトは、対象をインポートしているリポジトリ内のテストと、どのテストからもインポートされていないファイルを一覧表示します。該当するテストと、テストされていない領域のうち規模が大きい3つを読み、それぞれについて、ビジネスを支える振る舞い(お金、権限、失ってはならないデータ)を明示します。次に対象のエントリーポイントを列挙します。つまり、外部入力が対象に到達するすべての場所(HTTP ルート、CLI コマンド、メッセージハンドラー、パーサー、ユーザーデータを受け取るエクスポート関数)です。テスト以外で対象をインポートしている箇所を grep し、その先をたどって入力の境界まで確認します。完了条件は、各領域について、何をするのか、何に依存するのか、どのテストがすでに対象に触れているのか、エントリーポイントは何か、そこで間違えるとどのような損害があるのかを説明できることです。 - 提案する。 以下のテンプレートを使って、リポジトリで必要な手順、その順序、それぞれの理由を記した計画を作成します。メニューから選び、評価の結果、不要だと判断した手順は省略します。デフォルトではリファクタリングを計画の最後の手順とし、対象と理由を記載します。ユーザーから省略するよう指示された場合に限り、リファクタリングを省略します。完了条件は、選択したすべての手順に対象(ファイルまたは関数)と理由が記載されていることです。
- 一度だけ、承認を待つ。 計画を提示して待ちます。ユーザーは、計画がビジネスを支える振る舞いを対象としているか、テストが現在の実装ではなく振る舞いを検証しているかの2点を判断します。計画の承認には、リファクタリングの手順も含まれます。完了条件は、ユーザーが計画を承認するか、修正することです。
- 承認済みの計画を中断せずに実行する。 手順を順番に進めます。手順のプレイブックを開き、実行し、テストスイートを実行してから、次の手順に進みます。手順の間に「続けてもよいですか?」と尋ねてはいけません。計画が承認された時点で、その計画内のすべての手順が承認されています。停止して確認を求めるのは、ミュータントを同等と判定する場合、計画に記載されていない CI または設定を変更する場合、あるいはバグの疑いを特定するのではなく修正する場合に限ります。計測手順が目標を満たしてからリファクタリングを開始し、振る舞いを変更しないコミットで、テストとは別にコミットします。完了条件は、各手順の完了チェックに合格し、テストスイートがすべて成功することです。
- 最後に一度だけ報告する。 変更前後のスコア、追加したテスト、疑わしいバグ(クラッシュハントのプロパティテストで見つかったクラッシュも含む)、リファクタリングで変更した内容、未決事項を報告します。最初に、すべての一時ファイルを削除します。
メニュー
| 手順 | 使用する条件 | プレイブック |
|---|---|---|
| テスト可能にする | コードがデータベース、API、キュー、時計、またはファイルシステムに直接アクセスする | references/make-it-testable.md |
| 特性を記録する | コードが現在どのように動作するか、誰も説明できない | references/characterize.md |
| 承認テスト | 出力が大きい場合:JSON、HTML、レポート | references/approval-tests.md |
| プロパティテスト:クラッシュハント | 外部入力が到達できる各対象(エンドポイント、CLI コマンド、ハンドラー、パーサー)について、有効な入力と壊れた入力の両方を生成し、エントリーポイントが決してクラッシュしないことを確認する | references/property-testing.md |
| プロパティテスト:不変条件 | お金、日付、制限、往復変換など、エッジケースのあるルール | references/property-testing.md |
| 計測 | セーフティネットの完成とする前に、カバレッジで未実行の箇所を、ミューテーションテストで未検証の箇所を確認する | references/measure.md |
| リファクタリング | すべての計画で最後の手順とし、計測が目標を満たした後に実施する。ユーザーから指示された場合に限り省略する | references/refactor.md |
計画のテンプレート
Area: <files> — load-bearing: <behaviour>
Entry points: <route / command / handler / exported function> → <target>
1. <step> on <target> — why: <reason from the assessment>
2. ...
n. Crash-hunt property on <each entry point> — why: outside input reaches it; no input may crash it
n+1. Invariant properties on <rules> — why: <the rules with edge cases>
last. Refactor <target>: <the structure change> — why: <what the assessment found hard to read or change>
Skipped: <step> — why not needed here
Target: mutation score on <area> ≥ <n>% (coverage is a hint, not the goal)
注意事項
- 特性テストでは、バグも含めて現在の振る舞いを固定します。固定した結果がおかしいと思われる場合も、そのまま固定し、テスト名を
pins current behaviour — suspected bug: <what is wrong>として、最終レポートに記載してください。修正は別途、後の変更として行います。 - 依存関係が高速である場合や、ビジネスロジックを担っている場合は、テストでも実物を使います(インメモリまたはコンテナー化されたデータベース、実際のルールエンジンなど)。テストダブルを使うのは、低速または不安定な境界部分に限ります。
- 承認テストは足場です。機能テストで振る舞いをカバーできたら削除できるよう、ファイル名またはコメントで一時的なものだと明記してください。
- クラッシュ探索プロパティが見つけたクラッシュは、ユーザーがまだ遭遇していなくてもバグの疑いがあります。縮小後の入力を固定してリストに記載し、プロパティによる探索を続けてください。コードを読んでいるときに気付いたクラッシュで、プロパティを置き換えてはいけません。気付いたクラッシュを固定し、そのエントリーポイントにもプロパティを追加してください。
- 不安定なテストは安全網になりません。変更がないのに成功したり失敗したりするテストは、隔離してから、その上に作業を積み重ねる前に報告してください。
- 実行に時間のかかるテストスイートでは、作業中は対象範囲をカバーする最小のコマンド(1 ファイル、または 1 フォルダー)を実行し、各ステップの最後に全テストスイートを実行してください。
- ミューテーションスコアはリポジトリ全体ではなく、重要な領域を対象にしてください。リポジトリ全体の数値では、平均の中に重要なコードが埋もれてしまいます。
- ミューテーション実行の対象範囲を、毎回次の 2 通りで限定してください。対象ファイルのみをミューテートし(Stryker:
--mutate <file>)、そのファイルをインポートするテストのみを実行します(該当するテストはassess.sh <file>で一覧表示できます)。プロジェクトスクリプトがあればそれを使い、なければreferences/measure.mdの「スコープを限定した実行」を参照してください。リポジトリ独自の設定では、ドライランでテストスイート全体を実行できます。
最終検証日:2026-10-04。Claude Code 2.1.289 を使用し、TicketBay のブランチ legacy/reporting で計画のみ(実行なし)を実施しました。src/finance/payouts.ts では、2 件中 2 件の計画で GET /api/v1/organizer/payouts に対するクラッシュ探索プロパティと、最後のステップとしてのリファクタリングが、指示されていないにもかかわらず挙げられていました(前のバージョンでは 0 件中 2 件で、どちらも「スキップ:リファクタリング」でした)。src/finance/invoices.ts でも、1 件中 1 件の計画で同じことが行われていました。それ以前の 2026-10-04 には、TicketBay の src/db/admin-queries.ts で、ミューテーションスコアが 62.7% から 98.5% に上昇し、バグの疑いがある 4 件を固定しました。この試行を受けて今回のバージョンを作成しました。assess.sh はファイルパスを受け取らず、対象フォルダー外のテストを見落としていました。エージェントは各ステップの後に停止していました。スコープを限定しないドライランは 10 分経っても終わらなかった一方、スコープを限定した実行は 1.5 分で完了しました。それ以前の試行:2026-09-29、2.1.284(削除済みテストがある TicketBay、およびテストされていない実際の MERN アプリ)。