AI支援テスト:ユニットテストの門番でAI生成コードのリスクを包み込む

AI支援テストの完全なワークフローを共有します。設計文書からテストコードまで、ユニットテストの門番でAI生成コードのリスクを包み込み、チェックの対象をコードからテスト文書に変え、失敗したテストケースがAIの手抜きとエラーを監視します。

小さなバグは AI 支援テストの主戦場

バグは2つのタイプに分類されます:解きやすいバグと、解きにくいバグです。

解きにくいバグは通常、アーキテクチャや方針設計の欠陥から始まり、徐々に歴史的負債となり、一触即発の状態になります。サードパーティのライブラリ、オペレーティングシステム、コンパイラなどによって引き起こされることもあり、より専門的な知識と深い探索が必要です。この種の複雑な問題は本文の範囲外です。

本文は製品ライフサイクル内の小さなバグに焦点を当て、AIによるテストケース作成をサポートします。以下の問題を解決します:

  • すぐに発見できるバグ
  • 些細な修正で修復できる低レベルのバグ
  • 修正によって導入される新しいバグ
  • 緊急の修正時の中途半端なエラー
  • 対象テストのコストと効率のトレードオフ

多くのバグは小さなバグですが、小さなバグでも深刻な問題を引き起こす可能性があります。小さなバグをローカルで解決することで、その後の多くの労力を節約できます。

チェックの対象をコードからテスト文書に変える

私はかつて、AIに完全にタスクを任せ、生成されたコードを一切レビューしないという大胆なアイデアを試したことがあります。Copilot Agent 模式のような徹底的な放棄です。その結果、心に余裕がなくなりました。論理的な0と1の明確なプログラムで心に余裕がなければ、上流の信頼と顧客の信頼を失うことになります。そこで私は vibe coding に対する考察を始めました:AI生成のコードについては、常に心に余裕を持っておく必要があります。

レビューのコストが問題です。AIは sometimes 生成または修正するコード量が大きすぎます。3分で生成、2時間でレビューというのは珍しくありません。テストコードの作業量は通常、ビジネスコードよりも多く、開発の納品評価に必ずしも計上されないため、積極的にテストを書こうとしない人も少なくありません。しかし AI アシスタントは私より賢い ことは事実です。抗拒するよりも、門番を与えてあげるのが良いでしょう。

チェックの対象をコードからテスト文書に変えることは、この死角を回避する鍵です。ビジネスコードとは異なり、AI生成のテストケースは通常、非常に具体的なシーンを記述しており、レビューと検証が容易です。多くのケースは逐行読む必要さえなく、エラーが出たときに細かく見るだけで十分です。完全にAIコードをレビューしないのは少々極端ですが、十分なテスト文書があれば、すべてのコード行が人工レビューの必要があるわけではないことを証明できます。

---
title: コードから文書へのチェック
---
flowchart TB
    A[AI が大量のコードを生成] e1@--> B[逐行レビューのコストが高い]
    B e2@--> C{何をチェックする}
    C e3@-->|コード| D[2時間レビュー、心に余裕なし]
    C e4@-->|テスト文書| E[レビューが容易、素早い検証]
    E e5@--> F[ユニットテストの門禁]
    F e6@--> G[失敗ケースが問題を露呈]

    classDef risk fill:#FFF3E0,stroke:#E65100,color:#E65100;
    classDef ok fill:#E8F5E9,stroke:#2E7D32,color:#1B5E20;
    classDef animate stroke:#EF6C00,stroke-width:2px,stroke-dasharray: 9\,5,stroke-dashoffset: 900,animation: dash 25s linear infinite;
    class D risk;
    class E,F,G ok;
    class e1,e2,e3,e4,e5,e6 animate;

TDD/BDD/ATDD およびテスト可能なコード

3つのテスト駆動の違いは以下の通りです:

維度TDDBDDATDD
ドライブソースユニットテストユーザー行動シーン検収条件
参加者開発者ビジネス+開発ビジネス+テスト+開発
テスト層级ユニットテスト統合/エンドツーエンドテスト検収テスト
ツール例JUnit, pytestCucumber, SpecFlowFitNesse, Robot Framework
典型出力コードカバレッジレポート実行可能なユーザーシナリオ文書検収テスト合格率
言語assert add(2,3) == 5自然言語シナリオ (Given-When-Then)ビジネス言語 (検収条件表)

テスト可能なコードには4つの核心原則があります:シングル責任(各関数は1つのことだけをする)、依存性注入(依存関数はパラメータで渡す)、インターフェース隔離(入出力タイプは明確)、状態可観測(内部状態を検証するクエリインターフェースを提供する)。AIのプロンプトには、直接 拡張性、シングル責任、依存性注入 と書くことをお勧めします。以前、AIに生成する関数の長さやファイルの長さを制御させたことがありますが、従順さは低く、シングル責任と依存性注入に変えた後、生成されたコードは明らかに期待通りです。早すぎる最適化は万能ではありません。基本的なデータ構造とインターフェースを生成する際、AIに拡張性を優先するよう提示してください。

良いコードは静かに、悪いコードは存在感を強くします。テストしやすい関数は明確な戻り値を持ち、規定されたエラー処理、明確な引数、副作用の隔離、依存性注入に対応しています。

AIが出力するコードは常にベストプラクティスとは限らず、スタイルにも問題があります。問題を解決することはよくありますが、実際のプロジェクトは一度きりの買い切りではなく、保守が必要です。AIにコードを生成させる際は、読みやすさ優先、拡張性、シングル責任、依存性注入 も強調してください。コードスタイルの問題についてはこの際より多くの議論は行いません。Googleのコードスタイルガイド1を参考にしてください。

実際の開発でのつまずきとトレードオフ

要件は役割を越えて伝達される際、複数回の言語変換を経験します。それぞれの輪で表現の偏差が生じる可能性があります。例えば顧客が『一覧表示』と言った場合、バックエンドは『単一インターフェースで返却』と理解し、ページングを行わないことがあります。各役割の言語テンプレートをAIで生成することで、人的転換による情報損失を減らすことができます。

思考不足は多くのバグの根源です。AIでテストケースのシナリオを設計すると、期待タスクだけでなく、予期以上のヒントを与え、リリース指示者が考慮していない細節を埋めることができます。各役割の言語変換の輪では、AIと対話して漏洞を洗練させることができます。MarkdownはAIが最も理解しやすい言語なので、入出力は可能な限りMarkdown形式で行ってください。

テストケースにはいくつか注意すべきポイントがあります:テストケースの隔離、モック依存の正確性、アサーションの有効性、境界値、例外処理、パフォーマンス、並行性です。AIは手抜きをするため、一度にすべてのシナリオをカバーするテストケースを生成することはありません。モデルの要求が高いため、一度に全部のテストコードを生成するのは困難です。複数回の対話で補足することをお勧めします。

【Cursor 中の Claude モデル](/blog/cursor-automated-debugging/) を例にとると、テストケースコードに関する一部の注意事項は比較的よく果たしていますが、テストケースの隔離と例外シーンを忘れることがよくあります。user rule を一条追加すると少し手抜きが減ります:テストケース作成時はテスト文書に注意、テストケースの隔離。ただし、依然として一度にすべてのシナリオを生成することはなく、正常系、例外系、境界値、パフォーマンス、並行性などのシナリオは複数回の対話で補足する必要があります。

実用的な AI 支援テストワークフロー

各ステップのプロンプトは以下のように組織できます:製品設計文書に基づいて検収テストシナリオを生成;テストシナリオに基づいてデータ構造とインターフェースを生成;テストシナリオとインターフェースに基づいてエンド側テストケース表を生成;インターフェースとテストケーステーブルに基づいてテストケースコードを生成;テストシナリオに基づいてビジネスコードを生成し、テストケースが通過することを確認します。

ここで注意すべき落とし穴があります:AIはテストコードに基づいてビジネスコードを変更したり、逆にビジネスコードに基づいてテストコードを変更したりする可能性があります。両方にエラーがある場合、AIは混乱し、会話の回数が増えるにつれてテストコードとビジネスコードはますます乱雑になります。この際は人工介入が必要です。

貪欲すぎるプロンプトは反例です:AIに一度に正常、例外、境界値、並行性をすべてカバーしてコードを生成させようとするリクエストです。この種のリクエストは中断しやすいです。正しい方法は Cursor でシナリオを分割し、複数のリクエストで分割して生成することです。パフォーマンステストケースには大量のデータが必要なので、システムテストフェーズに回し、まず AI にランダムデータ生成器を書かせ、生成したデータでテストケースを構築します。

統合テストにはテスト環境の構築が必要です。一部のテストケースは外部ツールに依存します。例えばミクロアイソレーションは iptables と ipset に依存します。Docker でテストコンテナを構築することができます。原則は:人間がどのようにテストするか、それに合わせて AI に同じ自動化ステップを実行するように指示します。セキュリティテストについては現在、経験と分析に主に依存しており、成熟した AI サポートはまだありません。

まとめ

このワークフローは AI を使用しない場合と比較して、より多くの文書とテストコードを産出します。すべての文字を確認するのは困難です。しかし文書はコードより確認しやすく、特にテストケース表はテスト生成に正確なガイド役となります。実際の開発では、時折失敗したテストケースだけを確認し、失敗したテストケースをデバッグすることで問題を確認します。

改善の方向は3点です:設計フェーズのサブフローに重複があり、検収テストシナリオはデータ構造とインターフェースに依存する可能性があります。エンド側テストケース表は開発者自身が注目する必要があります。テスト実施前には、ビジネスコードの詳細を含む詳細なテストシナリオを再度生成する必要があります。

最終の収益はテストカバレッジの数字ではなく、信頼です。ユニットテストというローカルの門番を借りて、ようやく AI 生成のコードに対して心の底からの安心を持つことができ、適当に vibing できたコードを無批判に本番環境に送るのではなくなりました。


  1. Google Style Guide は Google が公開するコードスタイルガイドです。 ↩︎