こんにちは。CSIG の市川です。普段は FutureVuls という脆弱性・サプライチェーンリスク管理 SaaS の開発を担当しています。
今年から、初の試みとして「テスト連載」を実施します!🎉
AI 時代において、重要度が増すテスト技術、動作確認、品質保証などをテーマとした連載です。
今回は 5 人の方が参加します。
| 日付 | 執筆者 | タイトル/テーマ |
|---|---|---|
| 6/15(月) | 市川裕也さん | AI がテストを書く中で、「あるべき仕様」を単体テストで担保するために工夫してみていること |
| 6/16(火) | 澁川喜規さん | 生成AIに任せたら何でテストを書く?AIに聞いてみた |
| 6/17(水) | 武田大輝さん | TBD |
| 6/18(木) | 清水利博さん | 非機能テストを記号接地する |
| 6/19(金) | 佐藤尭彰さん | testlist の大切さと、testリストを割と柔軟に操作できる話 |
本記事は、テスト連載2026 の 1 日目の記事です。
はじめに
最近とあるソフトウェアを 2 か月くらいかけて 0 から AI で構築しており、この開発のなかで「テストを AI にどう書かせるか、どうやってテスト品質とコード品質を担保すれば良いか」を考えることが増えました。
今回はそのあたりで自分が考えていることを書き、実際にどのような運用を試してみているか(作成したプロンプト/skill や使用するタイミング、開発フローなど)を紹介します。
今回紹介する方法は、「AI で開発のスピードを出来る限り高めて、生産量を最大化する」という手法ではなく、品質を担保するためにある程度スピードを落とすものです。 (世間の流れからは逆行しているかもしれないです)
自分も AI とのかかわり方は随時アップデートしていきたいと考えているため、このやり方や思想の是非を含め、もしご意見あればぜひコメント等いただけますと幸いです。
本記事の中でもし参考になる部分があれば、必要な範囲で取り入れてみていただければと思います。
TL;DR
- AI が実装もテストも自律的に書くようになり、生成物を全部レビューするのは現実的でなくなった。品質を保つ手段として、単体テストが一層重要になる
- ただし「あるべき振る舞い」を決めるのは人間であり、テストはその仕様書。ここは AI に任せきれない、人間が握り続けるべき部分
- AI に任せると 2 つが崩れる。 (A) 振る舞いの記述が読みづらく、何を担保しているか人間が確認できない。 (B) 基準がないと写経テストや不要なテストが量産され、品質が下がる
- 対処として、「振る舞いの読みやすさのルール」と「何をテストし、何をテストしないか」を 1 つの markdown(
test-design.md)にまとめ、AI に常に読ませている - 一定の効果はあるが、確認の手間など課題も残っている
想定読者
- AI 時代において、単体テストの品質保証で悩んでいる人
- AI にどのようなテストを書かせるべきか / 書かせないべきかを悩んでいる人
- AI の出力する日本語が読みづらく、振る舞いを理解するのに困っている人
スコープ外の論点
- E2E テスト
- DDD 等の設計手法自体 (コードの品質を保つ、という観点ではこれも重要だと思いますが、本記事では扱いません)
- 単体テストのカバレッジの上げ方
- AI が、テストを通すためにテストを勝手に書き換えてしまう問題への対処方法
AI が自律して実装するようになった中で、品質をどう保つか
ここ半年ほどで、AI はかなり自律的に動くようになりました。plan mode で作りたい機能を渡して、出てきた実装計画を auto mode で実装させれば、コードもテストも最後まで自律して書いてくれます。
この変化により、単位時間あたりのコード生成量は大きく増えました。それに伴い、生成されたコードを全部読んでレビューするのは、認知負荷的にもスピード的にもかなり厳しくなってきました。
かといって、中身を見ずにソフトウェアに取り込み続けると、気づいたときには自分でも仕様が正しいか把握できない、変更容易性の低いソフトウェアが出来上がっている、ということになりかねません。
では、今までの何倍ものスピードで開発していくなかで、どうやって品質を保てばいいのでしょうか。色々観点はあるかと思いますが。自分は「単体テストにより品質を保つ」ことが重要なポイントの一つだと考えています。AI が量産するロジックの退行 (バグ) を最も簡単かつ継続的に検出できるのが単体テストだからです。
本記事では、AI 時代において、単体テストの品質を保つ方法について、持論を述べていきます。
前提と、自分が大事だと考えている 2 つの主張
前提: あるべき仕様は人間が決め、それを自動テストで継続的に担保する
まず前提として、あるべき仕様、あるべき振る舞いは人間が決めるものです。この部分は、AI 時代においても不変でしょう。(この前提がそもそもずれてきていたらすみません、コメント等いただきたいです)
そして、この「あるべき仕様、あるべき振る舞い」は、テストコードによって担保することが望ましいです。テストコードは、そのソフトウェアがどう振る舞うべきかを伝える、いわば「仕様書」でもあります。
品質を担保する上では、あるべき振る舞いが達成され続けているかを自動テストで継続的に担保していくことが、生成スピードが上がった今こそより重要になってくると思います。
この前提から導かれる 2 つの主張
AI にコード・テスト生成を任せながら、「自動テストで品質を継続的に担保する」ことを成立させるには、次の 2 つを満たす必要があります。
- A: 実装計画やテストコードから、あるべき振る舞いを把握しやすい形にしておく (振る舞いの記述の readability をあげる)
- B: そのテストコード自体の品質を保つ
何も対策をしないと、AI に任せたときに以下の表に記載したような課題が顕在化し、 A も B も崩れます。
この主張・課題と、課題への対処を、先に表にまとめておきます。
| あるべき姿 | AI に任せたときの課題 | どう対処するか | |
|---|---|---|---|
| A | あるべき振る舞いを、plan / テストコードから把握しやすくしておく | AI が出す「確認すべき振る舞い」の説明が分かりづらく、担保できているか人間が確認しづらい | 振る舞いの「読みやすさのルール」を決める |
| B | テストコード自体の品質を保つ | 品質の基準を与えないと、写経テストや不要なテストが量産され、保守性やリファクタリングへの耐性が下がる | 振る舞いをテストさせる /「何をテストし、何をテストしないか」を決める |
以降、A・B それぞれについて「なぜ大事か → AI に任せると何が問題か → どう対処してみているか」の順に見ていきます。
A: 振る舞いを把握しやすい状態にする
なぜ「把握しやすさ」が必要なのか
テストが「あるべき振る舞い」を担保できているかを人間が判断するには、まず、そのテストがどんな振る舞いを確認しようとしているのかが人間にとって把握しやすい状態でなければいけません。
テスト内容を人間が把握しづらい場合、「確認したつもり」で中身を流してしまい、結局は何も担保できていない、という状態になる恐れがあります。
課題A: AI が出力する「確認すべき振る舞い」は分かりづらい
何も指示しないと、AI が出力する「振る舞い」の説明は、英語を機械的に和訳しただけのような日本語になったり、実装の中身に踏み込んだ書き方になったりしがちです。
こうなると、その振る舞いの説明を読んでも「結局このテストは何を保証したいのか」が頭に入ってこず、人間がチェックする役割を果たせません。
対処A: 読みやすさのルールを決めておく
そこで自分は、 plan 時に振る舞いの説明を AI に書いてもらうようにし、その際次のような点を守って書かせるようにしています。
- コード内の変数名や内部のデータ構造を、説明にそのまま持ち込まない
- そのコードベースを知らない人(たとえばドメインに詳しい非エンジニア)が読んでも理解できる日本語にする
- 体言止めで止めず、「何をしたら、どうなれば正しいのか」を文章で言い切る
テストコード内のテストケース名やコメントも、同様の観点を守らせています。
この運用の効果については、「上記の考え方を、どのように開発フローに反映させているか」の節に記載しています。
B: テストコードの品質を保つ
前提: そもそも「品質の高いテスト」とは何か
テストにおいてどのような観点を大切にすべきかは、作っているソフトウェアによって異なると思います。
下記の観点には過不足があると思うので、あくまで参考として捉えていただければと思います。
結論から言うと、以下の 2 点は「品質の高いテスト」を満たすための重要な要素であると考えています。
- ① 実装の詳細に立ち入らず、振る舞いを確認していること
- ② テストする価値が高いコードに絞って、テストを記述していること
以下、『単体テストの考え方/使い方』という書籍を下敷きにし、この理由を記載します。
同書では、良い単体テストは次の 4 つの要素で評価できるとされています。
- 退行(≒ バグ)に対する保護
- リファクタリングへの耐性
- 迅速なフィードバック
- 保守のしやすさ
① なぜ振る舞いをテストする必要があるか
上記 4 点のうち、特に意識したいのが「リファクタリングへの耐性」です。
これはざっくり言うと「偽陽性の少なさ」です。偽陽性とは、実装は正しく振る舞っているのに、テストが落ちてしまう状態を指します。
偽陽性が増えると、開発者は次第にテストの結果を信用しなくなります。「また誤検知だろう」と落ちたテストを軽く扱うようになり、テストの存在意義が薄れていきます。また、何が正しい振る舞いかが分かりづらくなるため、変更容易性が低くなっていきます。
そして、この偽陽性は、テストが実装の詳細を確認してしまっているときに起きやすくなります。実装の詳細に立ち入っていると、実装の中身が少し変わっただけで、振る舞いは正しいのにテストが落ちる、という状況が発生しやすくなります。そのため、「実装の詳細」に立ち入らず、「振る舞い」のみを確認するテストを書くことが重要なのです。
② なぜテストする価値が高いコードに絞るべきか
これは、主に「保守のしやすさ」の観点で重要です。
「実装の中身をそのまま書き写した写経テスト」のような、取るに足らないテストが多いと、「肝心の振る舞いがちゃんと確認できているか」を人間が読み取る邪魔になります。そのようなテストは、段々保守されなくなっていきます。
継続的に保守していく、という観点において、「不要なテストを書かない」ことも重要です。
課題B: 基準を与えないと、写経テストや不要なテストが量産される
テストについて特に基準を与えないと、AI は次のようなテストを書きがちです。
- 実装の中身をそのまま書き写したような「写経」テスト
- 「振る舞い」ではなく「実装の詳細」を確認するテスト
先ほどの 4 要素で言えば、これらは「リファクタリングへの耐性」や「保守のしやすさ」を下げる、質の悪いテストです。
さらに、AI に任せると際限なくテストを増やせてしまうので、取るに足らないテストも大量に生成されがちです。
こうしたノイズが多いと、「肝心の振る舞いがちゃんと確認できているか」を人間が読み取る邪魔になります。テストは多ければ多いほど良い、というわけではありません。
対処B: 振る舞いをテストさせる /「何をテストし、何をテストしないか」を決める
そこで、「実装の詳細」ではなく、「振る舞い」をテストさせるようにします。
具体的には、
- 可能な限り出力値ベース・テストを優先する (状態ベース・テストやコミュニケーション・ベース・テストは最小限にする)
- 期待値はリテラルで直書きする
- モック系とスタブ系を使い分ける。外に出る副作用が存在しないときに、そのオブジェクトが呼ばれたかの検証は行わない
また、コードの性質によってテストの厚みを変えるようにしています。
- ドメインロジック / アルゴリズムは厚めにテストさせています。境界値、条件の組み合わせ、状態遷移、壊れやすい入力など、確認すべき振る舞いを網羅的に押さえます。ここはテストしやすく、かつバグが下流に波及しやすいからです。
- usecase 層 / コントローラ層の処理は、代表的なハッピーパスと、純粋ロジック側では確認できない「複数の処理を組み合わせたときにだけ起きる異常系」だけで十分、としています。個々の値の網羅は、純粋ロジック側で済んでいるはずだからです。
- それ以外の 取るに足らないコードはテストしないようにします。
「何をテストしないか」をはっきり決めておくことが、不要なテストの量産を抑え、結果として「担保すべき振る舞い」を見通しよく保つことに繋がります。
上記の考え方を、どのように開発フローに反映させているか
markdown にまとめる
A(振る舞いの読みやすさ)も B(テストの品質基準)も、毎回 AI に口頭で伝えるのは大変です。
そこで自分は、これらのテスト方針を 1 つの markdown(test-design.md)にまとめ、plan mode 時やレビュー時に AI に自動で読ませるようにしました。
実際に使っている test-design.md(特定プロジェクトに依存する記述を削った一般化版)
実物から、プロジェクトの構成やコードに依存する部分を取り除き、どのプロジェクトでも読めるように一般化したものです。雰囲気が伝わればと思って載せています。
**原則** |
運用フロー
この markdown を用いて、ざっくり次のような流れで運用してみています。
- Claude Code の plan mode を使用して plan を立てる。plan を立てる段階で、「どんな振る舞いをテストするつもりか/どんな振る舞いを確認しないと決めたか」を先に書き出させる
- その一覧を人間がチェックし、過不足がなさそうかを確認してから実装に進ませる
- 出来上がった plan を、他の AI(Codex など)にも
test-design.mdを物差しとして渡したうえでレビューさせる - Opus に実装させる
- plan を実装させた後のレビュー時にも、テストが
test-design.mdに沿っているかを他 AI に確認してもらう
出力はどう変わったか
「振る舞い」の記述について
test-design.md を入れる前と後で、AI が出してくる「確認すべき振る舞い」の説明は、自分の体感ではだいぶ読みやすくなりました。いくつか例を挙げます。
- 実装用語ではなく、外部から見た振る舞いで書くようになりました。
Before(実装・テスト用語で書いた、AI っぽい書き方): |
- 何をどう確認するのかを具体的に書くようになりました。
Before: |
- 体言止めせず、主語や条件を省略せずに動詞で言い切るようになりました。
Before: |
テストコードの品質について
テストコードについても、読みやすさと品質がどちらも向上しました。test-design.md を入れる前は、以下のようにメソッド名の対応表をテストにそのまま書き写しただけの、「誰かが値を変えたら落ちる」だけのテストがありました。
// 各画面が呼ぶべき API メソッド名を、対応表としてテストに固定する |
test-design.md を入れたことで、AI がこの写経テストを検出し、「識別子が重複していない」等の、壊れていたら必ずバグと言える性質だけを確認するテストに書き換えてくれました。
// 壊れていたら必ずバグになる、カタログの構造的な性質だけを確認する |
その他、この運用を試してみて感じたこと
「確認すべき振る舞い」を読んでいると、「これは自分が思っていたのと違う」と気づけるケースがそこそこありました。実装に入る前に認識のズレを潰せるのは嬉しいです。
また、「確認しないと決めた振る舞い」も一緒に出してもらうことで、「漏れているのではなく、意図して外したのだ」と分かり、一定の安心感が生まれました。
とはいえ、振る舞いの分量は依然として多く、確認するのはそれなりに大変です。「そもそも毎回律儀に全部の振る舞いを確認すべきなのか、ある程度は AI に任せてしまっても良いのではないか」というのは、別の論点としてあるなと思っています。
おわりに
AI を使えば、アプリケーションコードもテストコードも簡単に書けるようになりました。テストまで含めて勝手に生成される状況になると、つい楽な方に流れて、中身を確認しないまま取り込んでしまいがちです。
ですが、ソフトウェアに品質が求められる限り、その品質に責任を持つのは人間です。あるべき振る舞いを決める役割は、生成のスピードがどれだけ上がっても人間に残り続けると思っています。この役割が残る以上、「このソフトウェアはどう振る舞うべきで、そのためにどんなテストを書くべきか」を考えたり確かめたりする部分からは逃げずに、考えるべきなのではないでしょうか。
AI 時代におけるテスト品質の保ち方について、「自分のチームではこうしている」「ここはこう思う」といった意見があれば、ぜひコメント等いただけますと幸いです。