今日紹介するのは「ちょうどいいタイミングでバグを捕まえるテストを自動で作る」方法。テストを自動生成し、バグが本番環境に入り込む「前」に食い止める仕組みです。まずそもそもの話として、「テスト」って何のためにあるのでしょうか。ざっくり言うと、「コードがちゃんと動いているか確認するための、もうひとつのプログラム」です。

しかし現実のソフトウェアは複雑で巨大なので、人間が全部テストを書くことは大変です。
今回の内容は、大きく言うと2つの話が軸になっています。ひとつは「どうやってバグを見つけるテストを自動で作るか」というワークフローの話。もうひとつは「見つけたテスト結果をどう評価するか、本当にバグなのか誤報なのかをどう見分けるか」という成果と評価の話です。
以下で詳しく紹介します。
忙しい人向けに、重要なポイント5選
- コード変更の提出と同時にAIでテストを自動生成して、バグを本番に入る前に捕まえる仕組み
- 「この変更で何がマズくなりそうか」をAIに考えさせてからテストを作ると、従来手法の約4倍もバグ候補が見つかる
- 「本物のバグか誤報か」の判定にLLM3台の合議制とルールベースを併用し、人間のレビュー負荷を約70%減らした
- エンジニア41人に確認したところ8件が本物のバグで、うち4件は放置していたら深刻な障害になっていた
- エンジニアにテストコードは一切見せず「この変化、意図したもの?」と聞くだけなので、誤報でも数分で片付く
参照文献情報
- タイトル:Just-in-Time Catching Test Generation at Meta
- URL:https://arxiv.org/abs/2601.22832
- 著者:Matthew Becker, Yifei Chen, Nicholas Cochran, Pouyan Ghasemi, Abhishek Gulati, Mark Harman, Zachary Haluza, Mehrdad Honarkhah, Herve Robert, Jiacheng Liu, Weini Liu, Sreeja Thummala, Xiaoning Yang, Rui Xin, Sophie Zeng
- 所属:Meta Platforms
Just-in-Time Catching Test Generation at Meta
Metaにおけるジャストインタイムな捕捉テスト生成
テストを2つのタイプに分けて考える
今回は、テストを2つのタイプに分けて考えます。
ひとつは「ハードニングテスト」。ハードニングは「硬くする」という意味で、コードを頑丈にするためのテストです。作った時点ではちゃんとパス、つまり合格します。コードベースにずっと置いておいて、将来だれかがコードを変えたときに「壊れてないよね?」と見張ってくれる番犬のような存在です。世の中のテスト自動生成の研究は、ほとんどがこのハードニングテストを対象にしています。
もうひとつが、今回の主役である「キャッチングテスト」。こちらは作った時点で「わざと失敗する」テストです。失敗するテストに何の意味があるの?と思いますよね。これがミソです。
ハードニングテストは「将来のバグに備える」もの。一方キャッチングテストは「今まさに提出されたコード変更にバグがないか」を即座にチェックするためのものです。つまり、目的が違います。
エンジニアがコードの変更を提出する単位を「diff(ディフ)」と呼びます。「この修正を本番のコードに入れてください」という申請みたいなもの。キャッチングテストは、このdiffが提出された瞬間にテストを自動生成して走らせます。だから「Just-in-Time」、つまり「ちょうどそのタイミングで」という名前がついています。
さて、ここで大事な概念が2つ出てきます。「Weak Catch(弱いキャッチ)」と「Strong Catch(強いキャッチ)」です。
Weak Catch(弱いキャッチ)は「テストがdiff(差分)の上で失敗した」というだけの状態です。失敗したからといって、それが本当にバグを見つけたのか、テスト自体が間違っているだけなのかは、まだわかりません。たとえるなら、火災報知器が鳴ったけど、本当に火事なのか、料理の煙で誤作動しただけなのかわからない状態です。
一方Strong Catch(強いキャッチ)は「テストの失敗がちゃんと本物のバグを指している」と確認された状態。火災報知器が鳴って、実際に火事だった、というケースですね。
やりたいのは、Weak Catch(弱いキャッチ)をたくさん効率よく作り出して、そこからStrong Catch(強いキャッチ)を正確に見分けること。そして最終的に、深刻なバグが本番に入るのを防ぐことです。
ベースラインについて
ワークフローの本題に入る前に、「何と比べてどれくらい良いのか」を測るための基準を確認しておきます。
最初の基準は「Coincidental Catch(偶然のキャッチ)」です。ハードニング用に作ったテストが、たまたまdiff(差分)上でも失敗することがあります。本来パスするはずのテストが偶然キャッチになった、というケースです。ただ、先に結論を言うと、この偶然のキャッチは全テストのうちたった0.2%。あまりにも少なくて、実用にはなりませんでした。
次の基準は、既存のハードニング用ツールをそのままキャッチング目的に使ってみる方法です。
ポイントは、これらのツールはdiff(差分)の中身をまったく知らないということ。「今回どんな変更がされたか」を考慮せずにテストを作っています。だからこそ基準線、つまり「何も工夫しなかったらこれくらい」という最低ラインになるわけです。
diffを理解してテストを作る(ここが肝です)
ここからが一番面白いところです。「diff(差分)を知っている」ワークフローが2つ開発されました。順番に見ていきましょう。

「怪しいdiff」方式
1つ目は「Dodgy Diff(怪しいdiff)」。「Dodgy」は英語で「怪しい」という意味です。発想がとてもユニークで、エンジニアが提出したdiff(差分)を「バグが入ったバージョン」だと決めつけてしまう。もちろん実際にはバグがないことのほうが多いんですが、とにかくそう仮定して、変更前(親リビジョン)と変更後(diff)で振る舞いが違う部分を見つけるテストを大量に生成します。
ここで使う考え方が「ミューテーションテスト(変異テスト)」というものです。正しいコードにわざと小さな間違いを入れたものを「ミュータント(変異体)」と呼びます。そして「このミュータント(変異体)を見破れるテスト」を作ることで、テストの質を高める手法です。Dodgy Diff(怪しいdiff)では、diff(差分)そのものをこのミュータント(変異体)として扱います。つまり「diff=バグ入りのコード」という前提でテストを作る。乱暴ですが、これで「親と子で何かが変わった」ことを検知するテストが量産できます。
このワークフローはdiff(差分)の「意図」は一切読みません。なぜこの変更がされたのか、何を実現しようとしているのか、そういったことは考えない。ただひたすら「振る舞いの違い」を見つけるテストを量産する、いわば数で勝負する力技のアプローチです。
意図を読んでテストを作る方式
2つ目が「Intent-Aware(意図理解型)」ワークフロー。こちらはもっと頭を使うやり方です。
まず、LLMにdiff(差分)の内容を読ませて、「この変更を実装しようとしたとき、どんなミスが起きそうか」を考えさせます。たとえば「この関数の戻り値の型を変えているけど、呼び出し元でエラーが出る可能性がある」とか「この条件分岐を変えたら、特定のケースで想定外の動きをするかもしれない」といったリスクを文章として出力させる。
次に、そのリスクに基づいて「こんなバグが起きたら」というミュータント(変異体)を作ります。Dodgy Diff(怪しいdiff)がやみくもに差分を検知しようとするのに対して、Intent-Aware(意図理解型)は「こういうバグが現実にありそう」という推論をもとにしているので、より的を絞ったテストが作れます。
ちなみに、diff(差分)の「意図」を読むというのは、ソフトウェアテストの世界では「Oracle Problem(オラクル問題、つまり正解の定義が難しい問題)」と呼ばれるとても難しい課題に関わっています。オラクルというのは「正解を教えてくれる存在」のことで、「このコードの正しい振る舞いは何か」を定義すること自体が本質的に難しい、という問題です。LLMが登場する前は、こんなことを自動でやろうとすること自体が無謀だと思われていました。
しかしLLMの登場で、コード本体とdiff(差分)のタイトルやサマリーから、ある程度の意図を推論できるようになった。今回はまだシンプルなバージョンで、メールのやりとりやタスク管理ツールの情報までは使っていません。それでもすでに効果が出ているので、より豊かな情報を使えば今後さらに精度が上がるだろう、と研究者らは展望しています。
ワークフローの成績表
ここからはお待ちかねの数字の話です。今のワークフローが実際にどれだけ使えるのか、データで見ていきましょう。
2つのdiff対応ワークフローは2025年9月から本番運用されていて、ベースラインの非対応ワークフローは比較実験として2025年11月に10日間だけ動かしました。合計で22,126件のテストが生成されています。
まずCoincidental Catch(偶然のキャッチ)。全テストのうちWeak Catch(弱いキャッチ)になったのはわずか0.2%。ほぼ見つからないので、早い段階で実用から外されました。

次にハードニングベースライン。TestGen-LLM(LLMベースのテスト生成ツール)で約2.0%、ACH(ミューテーション誘導テスト生成ツール)で約0.8%がWeak Catch(弱いキャッチ)に。つまり、ハードニング用のテストをそのまま流用しても、だいたい100本に1本くらいはキャッチになる計算です。
そしていよいよdiff対応ワークフロー。Dodgy Diff(怪しいdiff)は2.5%、Intent-Aware(意図理解型)はなんと6.4%がWeak Catch(弱いキャッチ)になりました。ベースラインの3〜8倍です。
ここからがもっと大事で、「diff(差分)単位で見たとき、少なくとも1件でもキャッチできたdiffはどれくらいあったか」という数字を見てみると、ハードニングベースラインが1.6〜3.6%なのに対して、Dodgy Diff(怪しいdiff)は4.0%、Intent-Aware(意図理解型)は7.9%。Intent-Aware(意図理解型)はDodgy Diff(怪しいdiff)の約2倍のdiffでバグの可能性を検知できたことになります。しかもIntent-Aware(意図理解型)のほうが生成したテストの「数」自体は少ないのに、1本1本の命中率が高い。意図を読むことの効果がくっきり出ました。
本当に深刻なバグを防げるのか
数字は出揃いました。しかし一番大事な質問は「実際にバグ防げたのか?」です。
2025年9月中旬から10月末にかけて、チームは41人のエンジニアに連絡を取りました。といっても大げさなものではなく、チャットで「あなたのこのdiff(差分)で、こういう振る舞いの変化が見つかりました。これは意図したものですか?」とシンプルに聞いただけです。
結果、8件が「意図していなかった」、つまり本物のバグであるStrong Catch(強いキャッチ)だと確認されました。41件中8件なので、エンジニアから見た真陽性率は19.5%です。5回に1回は本物のバグが見つかる計算ですね。
そしてここがすごいのですが、この8件のうち4件は、もし見逃されていたら本番環境で深刻な障害を引き起こしていたことが事後分析で確認されました。サービスダウンやデータ不整合のような、大きな問題につながっていた可能性があるということです。
「しかし残りの33件は誤報だったのであればエンジニアに迷惑では?」と思うかもしれません。ここが巧妙に設計されています。
エンジニアに送るメッセージは、テストコードそのものではなく「この式の結果がtrueからfalseに変わりましたが、意図的ですか?」のようなシンプルな一文です。意図した変更なら「はい、想定通りです」と数分で返答するだけ。テストコードを読んだりデバッグしたりする必要はありません。
報告によると誤報を片付けるのにかかる時間はほんの数分で、エンジニアの仕事の邪魔にはほぼならなかったそうです。
一般的に、バグのうち深刻なものは全体の5〜20%くらいと言われています。でも今回は8件中4件、つまり50%が深刻でした。これはリスクの高いコード変更に絞ってテストを実行するという「ターゲティング(狙い撃ち)」が効いている証拠だと考えられます。
「本物」と「誤報」をどう見分けるか
ここまでの話で、Weak Catch(弱いキャッチ)を大量に作れることはわかりました。でも、鳴りっぱなしの火災報知器は誰も信用しません。誤報が多すぎたら、エンジニアは無視するようになってしまいます。だから「True Positive(真陽性、本物のバグ)」と「False Positive(偽陽性、テスト側の問題による誤報)」を自動で見分ける仕組みが不可欠です。
そこで、「アセッサー(自動判定器)」と呼ばれる仕組みが3種類作られています。
LLMに「裁判官」をやらせる
1つ目は、複数のモデルを「裁判官」として使う方法です。今回はLlama 3.3-70B、Gemini 3 Pro、Claude Sonnet 4という3つのモデルに同じ情報を渡して、それぞれに「このテスト失敗は本物のバグに見えますか?」と聞きます。アンサンブル(合議制)で1人の裁判官だけだと偏りが出るかもしれないけど、3人の意見を集めれば精度が上がるだろう、という発想です。
各モデルには、テストのコード、失敗時のエラーメッセージやログ、diff(差分)の内容、そしてAIが推論したdiff(差分)の意図が渡されます。そして「High(本物の可能性が高い)」「Medium(どちらとも言えない)」「Low(誤報の可能性が高い)」という3段階で判定を出します。
600件のWeak Catch(弱いキャッチ)で試した結果、最も確信度が低いものを自動的にFalse Positive(偽陽性、誤報)として除外することで、人間がレビューしなければならない件数を約70%減らせました。4倍のスピードで評価が回るようになったわけです。
パターンで見抜くルールベース方式
2つ目は「RubFake(ルールベース偽陽性除去ツール)」と名付けられたアプローチです。2025年10月から11月にかけて、エンジニアが600件のWeak Catch(弱いキャッチ)を実際に目で見て分析し、「こういうパターンの失敗はFalse Positive(偽陽性、誤報)だ」というルールを抽出しました。
たとえば、「テストの実行基盤そのものがクラッシュした」「AIがprivateメソッド(外部からアクセスしてはいけないメソッド)を無理やりテストしようとした」「AIが作ったモック(テスト用のニセのオブジェクト)が間違っていた」などがFalse Positive(偽陽性)のパターンとして見つかっています。
逆にTrue Positive(真陽性、本物のバグ)の兆候もルール化されています。「boolean(真偽値)がtrueからfalseに変わったのに、その定義式が直接変更されていない」「リファクタリング(動作を変えずにコードを整理する作業)なのに振る舞いが変わっている」などは、バグの可能性が高い兆候としてフラグが立てられます。

LLMベースの判定は柔軟だけど結果がブレる。ルールベースは融通が利かないけど結果が安定している。この2つは弱点を補い合う関係にあって、組み合わせて使うことで精度が上がるという形です。
判定者たちの成績を検証
この3つのアセッサー(自動判定器)は本当に信頼できるのか。人間のコードレビューアーが「Good(承認して本番に入れてOK)」か「Bad(差し戻し)」と判定したdiff(差分)を使って統計的に検証しています。
結果として、Goodなdiff(差分)に対してはアセッサー(自動判定器)がFalse Positive(偽陽性、誤報っぽい)と判定する割合が有意に高く、Badなdiff(差分)に対してはTrue Positive(真陽性、本物のバグっぽい)と判定する割合が有意に高いことが確認されました。
人間が「問題なし」と判断したdiffにはアセッサーも「誤報っぽい」と言い、人間が「問題あり」と判断したdiffにはアセッサーも「本物のバグっぽい」と言っている。人間の判断と方向性が合っている格好です。
面白いのは、3つのアセッサー(自動判定器)同士の一致度はそれほど高くなかったこと。これは一見問題に見えますが、実はいい兆候です。一致度が低いということは、それぞれが違う角度から判断しているということ。3つを組み合わせれば、どれか1つだけ使うより幅広くバグを見つけられる可能性があるのです。
うれしい副産物
ひとつ面白いおまけがあります。キャッチングテスト(バグを捕まえるテスト)を生成しようとする過程で、diff(差分)上で失敗せずにパスするテストも大量に生まれます。これらはそのままハードニングテスト(コードを頑丈にするテスト)、つまり「将来のバグに備える見張り番」としてコードベースに組み込めるんです。
Coincidental Catch(偶然のキャッチ、ハードニングからキャッチへの転用)はほぼ見つからなかったのに、逆方向のキャッチからハードニングへの転用は大量に見つかるという、きれいな非対称性があります。だったら最初からキャッチングを主目的にして、副産物としてハードニングテストも回収するのが一番効率的だよね、というのが提案です。一石二鳥ですね。
まとめ
今回の報告を整理します。
まず、diff(差分)の内容や意図を理解するワークフローによって、バグ候補を見つける効率がベースラインの4倍、偶然に頼る場合の20倍になったこと。とくに意図を読むIntent-Aware(意図理解型)ワークフローは、少ないテスト数でより多くのdiffのバグを検知できました。
次に、LLMベースとルールベースのアセッサー(自動判定器)を組み合わせることで、人間のレビュー負荷を70%カットしつつ、人間のコードレビュー結果と整合する判定ができたこと。3つのアセッサー(自動判定器)は互いに補完的で、組み合わせることでより信頼性の高い判定が期待できます。
そして最も重要なのが、41件のエンジニアへの確認から8件の本物のバグが見つかり、そのうち4件は本番で深刻な障害につながるものだったこと。エンジニアは基本的にテストコードを一切見る必要がなく、「この変化は意図的ですか?」と聞かれるだけで済みました。
エンジニアの仕事をほとんど邪魔せずに、深刻な障害を水際で食い止める。テスト自動生成の新しい良い方法論だと思います。
本記事の関連研究