powered by TechFeed
表示モード
ハウツー

AIエージェントに「テストを書いて」と頼むだけでは品質は保証できない — Google Cloudが提唱する5つのテストパターン

6月26日、Karl Weinmeisterが「Five essential testing patterns for AI agent development」と題した記事を公開した。コーディングエージェントにテストを書かせると、カバレッジの数字は上がる。しかし中身を見ると、AIは「テストが通ること」を最適化する——「アプリケーションの挙動を厳密に検証すること」ではない。「文字列が空でないことを確認する」「モックオブジェクトがハードコードされた値を返すことを確認する」といったテストが並ぶ結果、バグが隠れやすい「安全の幻想」が生まれる。本記事では、この逆説を解決するための5つのテストパターンをEコマースのチェックアウトサービスを例に解説している。

6月26日、Karl Weinmeisterが「Five essential testing patterns for AI agent development」と題した記事を公開した。コーディングエージェントにテストを書かせると、カバレッジの数字は上がる。しかし中身を見ると、AIは「テストが通ること」を最適化する——「アプリケーションの挙動を厳密に検証すること」ではない。「文字列が空でないことを確認する」「モックオブジェクトがハードコードされた値を返すことを確認する」といったテストが並ぶ結果、バグが隠れやすい「安全の幻想」が生まれる。本記事では、この逆説を解決するための5つのテストパターンをEコマースのチェックアウトサービスを例に解説している。


「テストを書いて」では足りない理由

問題の本質はシンプルだ。AIは「テストが通ること」を最適化する。「アプリケーションの挙動を厳密に検証すること」ではない。これが、バグが隠れやすい「安全の幻想」を生み出す。

Karl Weinmeister(Google Cloud)は、この問題を解決するための5つのテストパターンを、Eコマースのチェックアウトサービスを例に解説している。


パターン1:ハッピーパス(正常系)― まず土台を作る

最初に書くべきは、正常な入力で処理が完了することを確認するテストだ。ここで重要なのは Arrange-Act-Assert(AAA)構造 を明示的に指示すること。これにより、AIが「準備・実行・検証」を混在させず、明確に分離したテストを生成しやすくなる。

def test_checkout_happy_path():
    # Arrange: 依存関係とインプットのセットアップ
    mock_inventory = MagicMock(spec=InventoryService)
    mock_inventory.get_stock.return_value = 10
    mock_payment = MagicMock(spec=PaymentGateway)
    mock_payment.charge.return_value = True
    order = Order(
        inventory_service=mock_inventory,
        payment_gateway=mock_payment,
        customer_email="customer@example.com"
    )
    order.add_item(product_id="item_101", price=15.00, quantity=2)

    # Act: メインの処理を実行
    result = order.checkout()

    # Assert: 出力・状態変化・副作用を検証
    assert result == {"status": "success", "charged_amount": 30.00}
    assert order.is_paid is True
    assert order.status == "COMPLETED"
    mock_inventory.decrement_stock.assert_called_once_with("item_101", 2)

パターン2:サッドパス(異常系)― 最も見落としやすい

正常系が通るだけでは本番は乗り越えられない。決済ゲートウェイのタイムアウト、在庫切れ、ネットワーク障害など、現実のシステムは想定外の失敗に満ちている。

サッドパステストが検証すべき点は単なる「例外が出ること」ではない。失敗時にシステムが一貫した状態に戻ることだ。以下の例では、決済タイムアウト時に在庫のデクリメントが呼ばれておらず、注文状態がDRAFTのままであることまで検証している。

def test_checkout_payment_timeout_raises_error():
    mock_payment.charge.side_effect = Exception("Gateway Timeout")
    # ...
    with pytest.raises(PaymentFailedError) as exc_info:
        order.checkout()

    assert "Payment gateway error: Gateway Timeout" in str(exc_info.value)
    assert order.is_paid is False
    assert order.status == "DRAFT"
    mock_inventory.decrement_stock.assert_not_called()  # ← ここが重要

Weinmeisterは、Google Cloudの Developer Knowledge MCPサーバー を使って「何が失敗しうるか」のアイデアを広げることも提案している。


パターン3:エッジケース ― 境界値を体系的に網羅する

正常系・異常系の次に来るのが、境界値を狙い撃ちにするエッジケーステストだ。空のカート、マイナスの数量、DB上限を超える大量注文など、開発者が「まず起きないだろう」と思いがちな入力こそが本番障害の温床になる。

pytest.mark.parametrizeを使えば、複数の境界入力を1つのテストブロックで一括検証できる。AIエージェントへの指示にゼロ・負の数・最大文字列長といった具体的な境界条件を列挙することで、漏れのないパラメータセットを生成させやすくなる。

@pytest.mark.parametrize("price,quantity,expected_error_msg", [
    (-10.0, 1, "Price cannot be negative"),
    (15.0, 0, "Quantity must be greater than zero"),
    (15.0, -2, "Quantity must be greater than zero"),
])
def test_add_item_invalid_values_raise_value_error(price, quantity, expected_error_msg):
    ...

パターン4:シームテスト ― モジュール境界を検証する

Michael Feathersの著書 Working Effectively with Legacy Code で定義された「シーム(seam)」、つまり異なるモジュールが接続する境界面を対象にしたテストだ。

本物のDBを叩かずに、実際の依存関係の挙動を模倣するフェイクスタブFakeInventoryなど)を作成することで、統合レイヤーの振る舞いを検証する。単体テストではモックで隠れてしまいがちなインターフェース契約の崩壊を、シームテストが早期に検出する。AIエージェントが新しいモジュール間連携を追加した際には、このレイヤーのテストが自動的に要求されるよう指示に組み込んでおくことが有効だ。


パターン5:コンフォーマンステスト ― サードパーティ契約を守る

コンフォーマンステストは、サードパーティの決済プロバイダーなど外部アダプターがインターフェース契約(戻り値のスキーマ、ステータスコード、例外の形式)に準拠しているかを検証するものだ。

たとえばEコマースサービスが複数の決済プロバイダーをプラグインとして受け入れる構成の場合、新しいパートナーが加わるたびに各アダプターが同一の契約を満たしているかを確認しなければならない。単体テストや統合テストでは「自社コードが正しく動くか」を検証するが、コンフォーマンステストは「外部コードが約束どおりに振る舞うか」を検証する点で役割が異なる。AIエージェントが新しいアダプターを生成した際に、このテストを自動的に実行する仕組みを整えておくことで、サードパーティ起因の障害を早期に捕捉できる。


チームへの組み込み方

5つのパターンを個人の習慣ではなくチームの標準にするには、AIエージェントへの指示に直接埋め込むことが有効だ。Weinmeisterはリポジトリのルートに置く規約ファイル(AGENTS.mdなど)への記載を推奨しており、具体的には以下のような指示を追加することを提案している。

  • すべての基本的な機能テストにAAAストラクチャを義務付ける
  • 外部ネットワークやファイルシステムとのやり取りごとに、最低2つのサッドパスシナリオを要求する
  • エッジケースのfuzzingに際して、ゼロ・負の数・最大文字列長などの境界を明示する
  • 独立していた2つのモジュールが新たなインターフェース越しにデータをやり取りする場合は必ずシームテストを要求する

実際に動くコードで試したい場合は、Building with Google Antigravity Codelab が出発点として紹介されている。


AIがボイラープレートを書く比重が増すにつれ、開発者の役割は個々のアサーションを手書きすることから、システム全体の品質を設計・保証することへと移行しつつある。5つのパターンをエージェントへの指示として組み込むことが、その移行を支える実践的な手段になる。

詳細はFive essential testing patterns for AI agent developmentを参照していただきたい。