3行まとめ
観測。
入力。
出力。
比較。
テストが薄いコードは、直す前に今の挙動を証拠として残します。
- テストが薄いコードをCodexに直させる前に、まず現状の入力、出力、画面、手動確認を証拠として残します。
- characterization test、golden output、snapshot、manual checklistは役割が違います。全部をunit testへ寄せなくて大丈夫です。
- 修正後は「テストが通った」だけでなく、「守るべき既存挙動が残ったか」「変わった出力に承認があるか」を見ます。
古いコードをCodexに直してもらうと、作業は速くなります。読みにくい条件分岐を整理する。型を付ける。エラー処理を足す。古いAPI呼び出しを置き換える。人間が後回しにしていた仕事を進めやすくなります。
ただし、既存テストが薄いコードでは、速さがそのままリスクになります。Codexが「自然にきれいな実装」へ寄せた結果、実は利用者が頼っていた微妙な挙動を変えることがあります。仕様書に書かれていないが運用で使われている出力、古い画面の並び順、空値の扱い、例外時のログ、CSVの列順。こうしたものは、テストがなければ壊れても気づきにくいです。
この記事では、テストが薄い既存コードをCodexに触らせる前に、現状の挙動をどう固定するかを扱います。コード移行全体のcheckpointやrollbackは、公開済み記事のCodexでコード移行を進める前に決めることで扱いました。ここでは、その前段である「直す前に観測する」作業へ絞ります。
この記事でわかること
範囲。
証拠。
禁止。
確認。
AIに直させる前に、変えてよいものと守るものを分けます。
- characterization testをAI変更前に使う考え方
- snapshot、golden output、fixture、manual checklistの分け方
- read-only調査で入口と出口を見つける手順
- Codexへ渡す変更前パケットの型
- テストがない範囲を不明として残す方法
- 修正後にbefore/afterを比較する観点
- snapshot更新を人間承認へ戻す理由
- CIに入れる最小確認セット
この記事は、テストを書くべきかどうかの一般論ではありません。テストが十分ではない現実のコードを、どう安全に触り始めるかの話です。
前提知識
| 項目 | 内容 | 見方 |
|---|---|---|
| Character | 現状。 | |
| Snapshot | 構造。 | |
| Golden | 出力。 | |
| Manual | 手順。 |
正しさを証明できない時も、少なくとも変化は検出できます。
JestのSnapshot Testingは、出力された構造を保存し、次回実行時に差分を見る仕組みです。Playwright Testにもvisual comparisonやsnapshot系の確認があり、画面やaccessibility寄りの変化を検出できます。GitHub Actionsは、build、test、deployなどをworkflowとして実行できます。
これらは、Codex専用の仕組みではありません。ただ、Codexに修正を任せる前には相性がよいです。AIが大きく書き換える前に、今の挙動を観測できる形にしておくと、変更後のレビューが楽になります。
characterization testとは
characterization testは、理想仕様を証明するテストというより、今の挙動を記録するテストです。
古いコードでは、仕様書が古い、担当者がいない、誰も全体を覚えていない、ということがあります。その時に「正しい仕様」を最初から決めようとすると止まります。まずは、代表的な入力に対して今何が返るかを固定します。
snapshotとgolden outputの違い
snapshotは、UI構造やobject構造など、出力の形を保存して差分を見る時に向いています。
golden outputは、期待される出力fileや文字列を保存して比較する考え方です。CSV、JSON、Markdown、メール本文、API response、変換結果などに向いています。
どちらも万能ではありません。snapshotを丸ごと更新すると、壊れた変化も通ってしまいます。golden outputも、何を代表例にするかを間違えると守りたい挙動を守れません。
まず変更を止めて観測する
- 1Read
読む。
- 2Map
入口。
- 3Run
実行。
- 4Record
記録。
最初の仕事は修正ではなく、今の挙動を見える形にすることです。
テストが薄いコードでは、最初のCodex依頼を「修正してください」にしないほうが安全です。
最初は、read-onlyで観測します。どのfileが入口か。どの関数が出力を作るか。どのcommandで今の挙動を確認できるか。どこにfixtureを置けるか。先に地図を作ります。
read-onlyから始める
Codexには、最初に変更を禁止して調査させます。
例として、「まだfileを編集せず、対象機能の入口、出力、既存テスト、手動確認方法を一覧にしてください」と依頼します。
この段階では、改善案よりも根拠が大事です。対象file、呼び出し元、入力例、出力例、既存test、未確認領域を出させます。
入口と出口を決める
古いコードでは、入口と出口が分からないまま修正に入ると危険です。
入口は、API route、CLI command、UI event、batch job、library functionなどです。出口は、画面表示、DB更新、file出力、log、外部API request、return valueなどです。
Codexには、入口と出口を表にさせます。これだけで、どこを固定すべきかが見えます。
仕様と現状を分ける
仕様と現状は分けます。
仕様上はこうあるべき、現在はこう動いている。この2つを混ぜると、Codexが「正しそうな仕様」に合わせて既存挙動を変えることがあります。
まずは現状を記録し、変えるべき挙動は別に承認します。
証拠を4種類に分ける
| 項目 | 内容 | 見方 |
|---|---|---|
| Unit | 関数。 | |
| Integration | 連携。 | |
| Golden | 出力。 | |
| Manual | 人手。 |
全部をunit testにしようとせず、観測できる証拠を組み合わせます。
変更前の証拠は、unit testだけでは足りません。
古いコードほど、関数単位に切り出されていないことがあります。そこで、証拠をunit、integration、golden output、manual checklistに分けます。
unit
unit testは、関数や小さなmoduleの挙動を固定します。
入力が作りやすく、副作用が少ない場所に向いています。空文字、null、0件、重複、境界値、locale、timezoneなど、壊れやすい入力を選びます。
integration
integration testは、複数moduleを通した挙動を見ます。
API route、DBを使う処理、外部serviceのmock、CLI、batch jobなどです。全部を再現しようとすると重くなるため、代表flowに絞ります。
golden output
golden outputは、変換結果や生成物を保存して比較します。
たとえば、CSV importの結果、Markdown生成、invoice文面、API response、設定file変換、report出力です。人間が読めるfileとして保存すると、reviewしやすくなります。
manual checklist
manual checklistは、自動化できない確認を残す場所です。
古い画面、外部連携、本番に近い権限、社内admin操作などは、自動化が難しい場合があります。その時は、手動確認を「未検証」ではなく、明確な証拠として扱います。
手動確認を弱い証拠にしない
手動確認でも、手順、入力、期待結果、確認者、日時を残せば、レビュー材料になります。
「見た感じOK」ではなく、「この入力でこの画面が表示され、この出力fileが生成された」と書きます。
Codexへ渡す変更前パケット
| 項目 | 内容 | 見方 |
|---|---|---|
| Goal | 目的。 | |
| Current | 現状。 | |
| Allowed | 許可。 | |
| Forbidden | 禁止。 |
変更前パケットがあると、AIの善意の作り替えを抑えられます。
観測が終わったら、Codexへ変更前パケットを渡します。
これは長い仕様書ではありません。AIが変更中に守るべき境界を短くまとめたものです。
goal
goalには、今回の目的を書きます。
例: 「CSV importのnull処理を修正する」「古いAPI clientを新しいadapterへ置き換える」「画面の並び替え処理を読みやすくする」。
目的が曖昧だと、Codexは周辺の改善まで始めやすくなります。
current behavior
current behaviorには、今の挙動を書きます。
入力、出力、既存test、golden output、manual checklistを短く並べます。ここで「正しいか不明だが現状こう動く」と明記してもかまいません。
allowed change
allowed changeには、変えてよいものを書きます。
例: 「内部実装は変えてよい」「error messageのtypoは直してよい」「処理時間改善のためにloopを置き換えてよい」。
forbidden change
forbidden changeには、変えてはいけないものを書きます。
例: 「CSV列順を変えない」「既存API response fieldを消さない」「snapshotを承認なしに更新しない」「testをskipしない」。
テストがない範囲の扱い
不明。
入力。
画面。
記録。
未テスト範囲は、空白ではなくリスクとして扱います。
テストがない範囲を、なかったことにしないのが大事です。
Codexに任せると、未テスト範囲も自然に触れてしまうことがあります。だから、どこが未テストかを先に明示します。
不明を不明として残す
不明なものは、不明として残します。
「このbranchは古い顧客だけが使う可能性がある」「このflagの実運用が分からない」「このerror pathは再現できていない」。こうした情報は、修正前パケットに入れます。
不明を隠すと、Codexが普通のコードとして整理してしまいます。
先にfixtureを作る
fixtureは、入力例です。
CSV、JSON、HTML、log、API response、DB seed、画像、設定fileなど、対象機能が読む入力を残します。fixtureがあると、Codexの修正後に同じ入力で比較できます。
fixtureは完璧でなくてよいです。まず代表例を数個作り、バグ報告や運用例から増やします。
画面は操作と見た目を分ける
UIでは、操作と見た目を分けます。
操作はPlaywrightのlocatorやaccessibility寄りの確認で固定できます。見た目はscreenshotやvisual comparisonで確認します。どちらも必要ですが、混ぜると失敗理由が分かりにくくなります。
修正後の比較を決める
- 1Before
変更前。
- 2Patch
修正。
- 3After
変更後。
- 4Review
確認。
修正後は、test passだけでなく、守るべき出力が残っているかを見ます。
修正後に何を見るかを、修正前に決めます。
「通ったtestを見る」だけでは足りません。守るべき現状が残っているか、意図して変えた出力がreviewされているかを見ます。
before and after
before/afterは、同じ入力で比較します。
変更前の出力、変更後の出力、変わった理由、承認者を残します。差分がないことを期待する箇所と、差分が出るべき箇所を分けます。
snapshot update
snapshot更新は、必ずreview対象にします。
snapshotが落ちた時に、すぐ更新すると危険です。落ちた理由が、意図したUI変更なのか、壊れた構造なのかを見ます。
reviewer note
reviewer noteには、今回守った挙動、変えた挙動、未確認領域を書きます。
AIが作ったPRでは、diffだけでは意図が見えにくいことがあります。reviewer noteがあると、人間が差分を追いやすくなります。
snapshot更新の承認
snapshot更新は、軽い変更に見えても承認対象にします。
特にUI、API response、CSV、メール本文、契約書、請求書、通知文面のsnapshotは、利用者影響があります。Codexに「snapshot更新は人間承認なしに行わない」と渡します。
CIに入れる最小セット
| 項目 | 内容 | 見方 |
|---|---|---|
| Fast | 軽い。 | |
| Risky | 重い。 | |
| Manual | 人手。 | |
| Artifact | 証跡。 |
CIには、軽い自動確認と人が見る証跡を分けて入れます。
最初から大きなtest suiteを作る必要はありません。
まずは、毎回走る軽い確認、必要時に走る重い確認、人が見るmanual evidenceを分けます。
fast check
fast checkは、PRごとに走る確認です。
unit test、対象moduleのtest、typecheck、lint、golden output比較などです。数分で終わるものを入れます。
risky check
risky checkは、影響が大きい時に走る確認です。
E2E、DBを使うintegration、visual comparison、外部serviceのmock、migration rehearsalなどです。毎回重すぎるなら、labelやmanual triggerで分けます。
manual evidence
manual evidenceは、CIで自動化できない証拠です。
screenshot、操作手順、出力file、確認URL、ログ抜粋、QAメモなどをPRへ添えます。GitHub Actionsのartifactやjob summaryへ残すのも有効です。
よくある失敗
作り替え。
丸更新。
入力なし。
理由なし。
テストが薄い時ほど、きれいな大改修より小さな比較を優先します。
よくある失敗は、テストが薄いからといって、いきなり大きく作り替えることです。
1つ目は、Codexに「きれいにして」と頼むことです。きれいさは人によって違います。目的、守る出力、禁止変更を渡します。
2つ目は、snapshotを丸ごと更新することです。snapshot更新は差分を消す操作でもあります。なぜ変わったかをreviewします。
3つ目は、fixtureなしで修正することです。同じ入力で比較できなければ、直ったかどうかが分かりません。
4つ目は、未テスト範囲をPR本文に残さないことです。分からない範囲を残せば、reviewerが判断できます。
FAQ
| 項目 | 内容 | 見方 |
|---|---|---|
| 何から? | 入口。 | |
| 何件? | 代表。 | |
| 更新? | 承認。 | |
| 捨てる? | 理由。 |
迷ったら、変更前後で説明できる証拠があるかへ戻ります。
テストが全くない場合は何から始めますか
入口と出口を1つ選びます。
まず代表入力を作り、今の出力を保存します。unit testが難しければ、CLI実行、API response、生成file、手動確認でもかまいません。
characterization testは正しくない挙動も固定しませんか
固定します。だから、名前やコメントで「現状の記録」と分かるようにします。
バグとして直す挙動は、別のtestで期待値を変えます。現状固定と仕様修正を同じtestに混ぜないほうがreviewしやすいです。
snapshotは使わないほうがいいですか
使えます。ただし、更新を軽く扱わないことが大事です。
snapshotは広く変化を拾える一方で、差分の意味を人間が見ないと危険です。特にCodexが修正したPRでは、snapshot更新理由をPR本文へ残します。
Codexにテスト追加まで任せてよいですか
任せてよいです。ただし、先に「現状を固定するtest」と「仕様変更を確認するtest」を分けます。
AIが書いたtestが実装に寄りすぎていないか、fixtureが代表例として妥当か、未確認領域が残っているかを人間が見ます。
参照した主な情報源
- Snapshot Testing | Jest
- Visual comparisons | Playwright
- Assertions | Playwright
- Workflow syntax for GitHub Actions | GitHub Docs
- GitHub Actions quickstart | GitHub Docs
次に読むなら
更新履歴
- 2026.05.31
初版。
テストツールの詳細は、導入時に公式docsで見直します。
- 2026.05.31 初版公開。
