AIコーディングを効率化するテスト開発技法

JetBrainsのJunieなどAIエージェントにコーディングをおまかせすると、単一コードとしては高品質でもシステム全体と整合性が取れていないことがよくあります。それを防ぐために、テストを有効活用しましょうというお話。

効率よくバイブコーディングする術を身に着けてしまって1日でJetBrains AI Ultimateライセンス 月の15%のクオータを1日で消費してしまうきのこ先輩ですこんにちは。

公式発表ではクオータを使い切っているユーザは全体の1%未満なんだそうで、私の使い方はもしかしたら異常なのかもしれないと思う今日このごろです。毎回サポートにおかわり依頼するのもアレなので、もうちょっと初期クオータ量を増やしてもらえないでしょうかねぇ。。

AIコーディング特有のクセ

ClaudeもChatGPTもGeminiも、大規模言語モデルを使ったAIには扱えるトークン量≒文字数には限界があります。
(厳密に言うとTransformerのシステム上はトークンの限界はないんですけど、多すぎると生成の精度が指数的に落ちていくのでサービス側で制限しています。)

そういった性質をふまえて、AIだけでバイブコーディングしているとプロジェクトに特有のクセが出てきます。

AIは記憶できない

ソースコードが100ファイルを超えるくらいの規模になると、全体では数万行・数百万文字になりますよね。

こうなってくるとAIではシステム全体を把握することができなくなり、なんの工夫もしなかった場合はシステムの整合性を取ったコーディングができなくなっていきます。

まさか令和の今になってコードの行数を気にする必要が出てくるとは思ってなかったですね。。

AIは見える範囲で作業しようとする

AIに与える情報は適度なトークン量が望ましいというのはAI自身も良くわかっているので、明確な指示や必要性がなければ他のファイルを読み込んだりしない傾向です。そのためなるべく1ファイルで収めようとして1000行超えのソースコードを作ったりします。

改めて責任ベースで分離を指示することになるのですが、このときも

  • 同じような機能が他のソースコードにないか
  • 紛らわしい命名になっていないか

というのはAIはあまり見ていないです。


まぁこういった諸問題を、テスト駆動開発で取り除いていこうというのが今回のお話の趣旨です。

テストドリブンとテストファースト

「テスト駆動開発」(Test Driven Development; TDD)というのはSEだとよく聞く単語ですけど、テストの取り扱い方についてはわりと人によって解釈が違ったりします。

  1. コードをまとめて書いて、コミットする前にユニットテスト
    • 通ったらコミット
  2. コードを書きながら関数1個ずつユニットテスト
  3. ユニットテストで仮実装、通ったら本実装
  4. まだ存在しない機能のテストを先に書いて本実装

開発スタイルで向き不向きがあると思いますので、認識が違うのはしょうがないところだとは思いますが、この記事の中でのTDDは上の4番、テストファーストなスタイルで読んでもらえれば。

テストドリブンとAIの相性

今更ですけど、テストファーストで開発するメリットですが、

テストを読めば何をする目的の関数なのかがわかる

これ。ユニットテストコードが仕様書として機能するようになります。そしてこれはAIにとっても同じことで、元のソースコード全部を読まなくても関数の動作を推測できるようになります。

不具合調査が速くなる

そもそもバグの発生を抑える目的でテストドリブン開発をするのですけど、それでも「見えないバグ」というのは発生するものです。音楽系ソフトだと「数値的には正常だけど音楽的には異常」とかよくあります。チャメシ・インシデントです。

人間の感覚に関わる判断はAIは得意ではないんですけど、具体的に「処理後にC4になるべきところがF4で鳴っている」とか言語化してあげると理解できるようになります。後は「まずC4とは何か、F4とは何か……(ネットリ」みたいなテストがあれば、AIでもバグとして認識し、修正できるようになります。

結合テストでシステム全体の整合性がとれる

トークン量の都合でAIが苦手とする「広範囲に影響があるコード」も、結合テストで問題が出たらすぐに分かるようになります。AIエージェントにとってもエラーが起こっていることがわかりやすいので、即修正できるようになります。

Junieにテストドリブン開発を仕込む

Junieに「テストファーストでやって」と毎回プロンプトで指示してもいいんですけどめんどくさいので、.junie/guidelines.mdにあらかじめ記載しておくと良いと思います。私のプロジェクトだとこんな感じです。

XX.1) TEST-FIRST ENFORCEMENT
MANDATORY: Execute cargo test --quiet before ANY code generation or changes
FORBIDDEN: New feature development when failed tests > 25
REQUIRED: Immediate fix of any newly introduced test failures
REPORTING: Always report test count changes (before → after)
QUALITY GATE: Test failure threshold ≤ 25 failed tests maximum
ZERO TOLERANCE: Clippy warnings must be resolved before proceeding

XX.2) CODE GENERATION SEQUENCE
Junie must follow this exact sequence for ANY code changes:

Check existing test status (cargo test --quiet)
Fix any failed tests if > 25 failures (BLOCKING requirement)
Create new tests for requested feature (TEST-FIRST)
Implement minimum viable solution to pass tests
Verify all tests pass (cargo test --quiet)
Run clippy validation (cargo clippy --all-targets --all-features -- -D warnings)
Enhance implementation iteratively while maintaining test coverage

---
意訳
XX.1) テストファーストを強制
義務:cargo test --quiet をコード生成の前に実行する
禁止:テスト失敗が25以上あるときは新機能の実装を中止
必要:テスト失敗したらすぐに修正
報告:常にテストの数の変化を報告する
品質保証:テスト失敗数は25以下に保つ
厳密:clippy(Rustの文法チェッカー)の警告も修正する

XX.2) コード生成の流れ
Junieはコード修正の際に必ずこのシーケンスに従う:
既存テストの状態を確認する(cargo test --quiet)
25以上テスト失敗している場合は指示以外のものも修正する
要求された機能には新しくテストを作成する(テストファースト)
テストに成功するための最小限のソリューションで実装する
全テストの成功を確認する (cargo test --quiet)
clippyで文法チェックする
テストカバレッジを維持して実装を強化していく

この他、プロジェクトの構成にあわせて、具体的なテストのコマンドも記載していきます。Rustだとcargo test tests::services --verboseとか。結構な文章量がありますので、これもJunie自体に「テストファーストで開発するルールをガイドラインに追加して」とかで書かせてもいいんじゃないでしょうか。


AIエージェントでTDDするデメリット

メリットばっかり並べてますが、それじゃなんでAIエージェントでコーディングするときにTDDがデフォルトじゃないかというと、デメリットもあるんですよね。この記事のシメとして、AIコーディングでTDDをやるデメリットも書いておこうと思います。

まぁこれは人間がTDDでやっても同じなんですけど。

遅い

特にスクラッチで機能を作るときとか、「何をどう実装するか、実験しながら設計する」というとき。テストファーストはかなり相性が良くないです。

バイブコーディングするときは結構ふわっとした指示を投げがちなので、Askモードでしっかり設計を考察させてから実装させたほうがいいでしょう。

特に指示しなければ、AIはテストコードなしでゴリゴリ書いて簡易テストだけで

👈️ヨシ!

となりがちです。

まぁそれで問題なく動くならそっちのほうが速いのは間違いないです。ソースコードが10個程度の小規模なプロジェクトだったらわざわざTDDにするメリットは薄いでしょう。

ブーストしにくい

ブーストというのは複数のプロンプトを同時に実行することを指します。Junieでも、プロンプト実行中に、左上の「←」をクリックすると新しいチャットを打つことができて同時作業させることもできます。

ブーストの例

他のAIエージェントはわからんのですがJunieの場合、「同じファイルを複数のプロンプトで編集しようとすると止まる」問題があります。ネットワーク共有フォルダのExcelでよくあるやつ感。。

テストドリブンにするとどのプロンプトも全体テストして失敗しているものを直そうとするので、編集が競合して止まりがちです。WorkingでJunieアイコンがクルクルしてるのにDoneが出てる状態がそれです。

編集が競合して止まっちゃった例

一旦Stopボタンで止めて、「続けてください」のプロンプトを与えれば続きを実行できますが、テスト駆動でJunieを使う場合はCodeモードは1個だけにしたほうが気持ちよくバイブコーディングできます。

ちなみに編集じゃなければいくらでも重複して実行できますので、Askで実装計画を作らせるとか、デバッグ調査させるとかはジャンジャンプロンプトを投げて大丈夫です。

トークンめちゃ使う

テスト駆動じゃない場合のコーディングの流れは

  1. 既存コードの実装確認
  2. 機能の実装設計
  3. コード作成
  4. 簡易動作テスト
  5. 実行内容解析
  6. コード修正
  7. 簡易動作テスト
  8. (完了)

テスト駆動のコーディングの流れは、

  1. 既存テストの確認
  2. 既存テストの実行チェック
  3. 機能とテストの設計
  4. テスト作成
  5. 機能のコード作成
  6. ユニットテスト実行
  7. 実行内容解析
  8. コード修正
  9. ユニットテスト実行
  10. 実行内容解析
  11. 統合テスト実行
  12. 実行内容解析
  13. コード修正
  14. 統合テスト実行
  15. 実行内容解析
  16. (完了)

特に大きなエラーがなかった場合でも倍以上の作業量になっちゃいますね。

それゆえトークンの消費量も莫大に……。冒頭で話してた1日で月の割当の15%も消費したのは多分TDDに変更したのも大きいと思います。バイブコーディングが重課金が前提になるのでデフォではガチガチのTDDはやらないようになっているのかもです。

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です