【GitHub Copilot活用】API設計書から統合テストを自動生成して工数70%削減した話

GitHub CopilotでAPI統合テストを自動生成するイメージ

リード文

REST APIの統合テスト、手作業で書くと1エンドポイント30分かかるやつ。GitHub Copilotに任せたら15のエンドポイントを2時間で完了し、工数を約70%削減できました。実際に使ってみて気づいたのは、「API設計書をコンテキストとして渡す」ことの重要性。この記事では、GitHub CopilotでAPI統合テストを効率的に自動生成するプロンプト設計と実践テクニックを解説します。

背景・課題:統合テストの作成が開発のボトルネックに

マイクロサービス化を進めているプロジェクトで、REST APIのエンドポイントが増え続けていました。新機能リリースのたびに統合テストを追加する必要があるのですが、これが想像以上に時間がかかる。

従来のやり方の問題点:

  • リクエストボディのテストデータを手作業で用意するのが面倒
  • 正常系だけでなく異常系(400/401/404/500エラー)も網羅する必要がある
  • 認証トークンの準備やモックの設定などボイラープレートコードが多い
  • エンドポイントが増えるほど、テストコードの保守コストも増大

1エンドポイントあたり平均30分かかり、15エンドポイントで7.5時間。これを短縮したくて、GitHub Copilotを試してみることにしました。

使用したツール・環境

使用ツール: GitHub Copilot(VSCode拡張機能版)
選んだ理由: エディタ内で完結し、既存のコードベースをコンテキストとして活用できるため

環境:

  • 言語:TypeScript
  • テストフレームワーク:Jest + Supertest
  • APIフレームワーク:Express.js
  • API設計書:OpenAPI 3.0(Swagger形式)

前提条件:

  • API設計書(OpenAPI仕様)が用意されていること
  • 既存のテストコードが1〜2ファイル程度あること(パターン学習のため)
  • GitHub Copilot Pro以上のプランに加入していること

実際に使ったプロンプト

使用したプロンプト

// 以下のAPI設計書に基づいて、統合テストコードを生成してください
// 
// OpenAPI仕様:
// - エンドポイント: POST /api/v1/users
// - リクエストボディ: { name: string, email: string, age: number }
// - 正常時レスポンス: 201 Created, { id: string, name: string, email: string, age: number, createdAt: string }
// - 異常時レスポンス: 
//   - 400 Bad Request (バリデーションエラー)
//   - 401 Unauthorized (認証エラー)
//   - 409 Conflict (メールアドレス重複)
//
// テスト要件:
// 1. 正常系: 有効なデータでユーザー作成が成功すること
// 2. 異常系: 必須フィールドが欠けている場合に400エラーを返すこと
// 3. 異常系: メールアドレス形式が不正な場合に400エラーを返すこと
// 4. 異常系: 認証トークンがない場合に401エラーを返すこと
// 5. 異常系: 既存のメールアドレスで登録しようとした場合に409エラーを返すこと
//
// 既存のテストコード(users.test.ts)のスタイルに合わせて生成してください
// - describe/itブロックの構成
// - beforeEach/afterEachでのデータベースクリーンアップ
// - expectのアサーション形式

describe('POST /api/v1/users', () => {

プロンプト設計のポイント

このプロンプトで工夫した点は以下の5つです:

1. OpenAPI仕様を明示的に記載

API設計書の情報をプロンプト内に直接書くことで、Copilotがリクエスト/レスポンスの型を正確に理解できるようになりました。特に「リクエストボディの必須フィールド」「レスポンスステータスコードごとの挙動」を書くと精度が上がります。

2. テストケースを5つのカテゴリに分類

「正常系1つ + 異常系4つ」という構成を明示することで、エッジケースまでカバーしたテストが生成されます。特に401/409など見落としがちなエラーケースを指定するのが重要。

3. 既存コードのスタイルを参照させる

「既存のテストコード(users.test.ts)のスタイルに合わせて」と書くことで、プロジェクト固有の命名規則やアサーション形式を踏襲してくれます。Copilotは開いているファイルをコンテキストとして学習するため、参照ファイルを開いておくのがコツ。

4. データベースクリーンアップを明記

統合テストでよくあるのが「テストデータの残骸で次のテストが失敗する」問題。beforeEach/afterEachでのクリーンアップをプロンプトに含めることで、テストの独立性を保てます。

5. describeブロックの開始を書いてからCopilotに続きを任せる

プロンプトの最後に`describe(‘POST /api/v1/users’, () => {`と書き、そこからCopilotに補完させると、流れるようにitブロックが生成されます。コメントだけよりも、実際のコードの開始部分を書く方が精度が高いです。

実行結果・効果:工数70%削減を達成

Before: 15エンドポイント × 30分 = 7.5時間
After: 15エンドポイント × 8分(生成3分 + レビュー5分)= 2時間

削減時間: 5.5時間(約70%削減)

実際に生成されたテストコードの一部を紹介します:

describe('POST /api/v1/users', () => {
  let authToken: string;

  beforeEach(async () => {
    await cleanupDatabase();
    authToken = await generateTestAuthToken();
  });

  it('should create a new user with valid data', async () => {
    const userData = {
      name: 'Test User',
      email: 'test@example.com',
      age: 25
    };

    const response = await request(app)
      .post('/api/v1/users')
      .set('Authorization', `Bearer ${authToken}`)
      .send(userData)
      .expect(201);

    expect(response.body).toHaveProperty('id');
    expect(response.body.name).toBe(userData.name);
    expect(response.body.email).toBe(userData.email);
    expect(response.body.age).toBe(userData.age);
    expect(response.body).toHaveProperty('createdAt');
  });

  it('should return 400 when required fields are missing', async () => {
    const invalidData = {
      email: 'test@example.com'
      // name and age are missing
    };

    const response = await request(app)
      .post('/api/v1/users')
      .set('Authorization', `Bearer ${authToken}`)
      .send(invalidData)
      .expect(400);

    expect(response.body).toHaveProperty('error');
  });

  // 以下、メール形式エラー、認証エラー、重複エラーのテストケースが続く...
});

想定外だった良い点:

  • テストデータのバリエーションも自動で用意してくれる(境界値テストのデータなど)
  • エラーメッセージのアサーションまで含めてくれる
  • コメントで「なぜこのテストが必要か」を説明してくれる

応用パターン・カスタマイズ例

このアプローチは他のユースケースにも応用できます:

1. GraphQL APIのテスト生成

OpenAPI仕様の代わりにGraphQLスキーマを渡せば、mutation/queryのテストが生成できます。プロンプトの「エンドポイント」部分を「mutation createUser」に置き換えるだけ。

2. E2Eテスト(Playwright)への応用

Supertestの代わりにPlaywrightを使ったE2Eテストも同様に生成可能。「ブラウザ操作の流れ」をプロンプトに記載することで、UI操作を含むテストが作れます。

3. 既存APIのリグレッションテスト追加

レガシーなAPIで「テストがない」状態から始める場合、実際のAPIレスポンスをcURLで取得し、それをプロンプトに含めることで、現状の挙動を保証するテストを一気に作成できます。

注意点・限界:人間の確認が必要なケース

便利なGitHub Copilotですが、以下の点には注意が必要です:

1. ビジネスロジックの背景は理解できない

「ユーザー登録時に招待コードが必要」のような、ドメイン固有のルールはプロンプトに明記しないと反映されません。生成後に手動で追加が必要です。

2. 複雑な認証フローは手動調整が必要

OAuth 2.0やSAML認証など、多段階の認証フローは自動生成が難しい場合があります。トークン取得部分は既存のヘルパー関数を参照させると良いです。

3. モックの設定は別途必要

外部APIやデータベースのモック設定は、Copilotが自動で用意してくれません。beforeEachでのモック初期化コードは人間が書く必要があります。

4. テストの実行順序依存に注意

生成されたテストが「前のテストのデータに依存している」ケースがあります。必ずbeforeEach/afterEachでクリーンアップし、テストの独立性を保ちましょう。

まとめ

  • API設計書をプロンプトに含めることで、統合テストの工数を70%削減できた
  • 正常系だけでなく異常系(400/401/409エラー)も明示することで、エッジケースまでカバー
  • 既存コードのスタイルを参照させることで、プロジェクトの命名規則を踏襲したテストが生成される

まず試してほしいこと:
既存のテストファイルを1つ開いた状態で、新しいエンドポイントのAPI設計書をコメントとして貼り付け、describeブロックの開始部分だけ書いてみてください。Copilotが続きを補完してくれるはずです。最初の1つが成功したら、残りのエンドポイントも一気に作成できるようになります。