サイトのAPI図鑑B版
掲載情報が正確でない可能性があります。
開発者向けAPIツール

CI/CDパイプラインでのAPIテスト自動化【GitHub Actions・Jest・Supertest】

GitHub ActionsとJest・Supertest・Pytestを使ったAPIの統合テスト・E2Eテストの実装方法と、CI/CDパイプラインへの組み込み手順を解説します。

#CI/CD#GitHub Actions#APIテスト#Jest

CI/CDでのAPIテスト自動化の重要性

APIの変更がデグレードを引き起こさないことをコードプッシュのたびに自動的に検証するCI/CDパイプラインは、高品質なAPIを継続的にリリースするための基盤です。JestとSupertestを使ったNode.js APIのテスト自動化から、GitHub Actionsへの組み込みまでを解説します。

Supertestを使ったExpressテスト

// tests/api/users.test.ts
import request from 'supertest';
import { app } from '../../src/app';
import { db } from '../../src/db';

describe('GET /api/users', () => {
  beforeEach(async () => {
    await db.users.create({ 
      data: { id: 'test-1', name: 'テストユーザー', email: 'test@example.com' }
    });
  });
  
  afterEach(async () => {
    await db.users.deleteMany({});
  });
  
  it('認証なしでは401を返す', async () => {
    const res = await request(app).get('/api/users');
    expect(res.status).toBe(401);
  });
  
  it('認証済みユーザー一覧を取得できる', async () => {
    const res = await request(app)
      .get('/api/users')
      .set('Authorization', `Bearer ${TEST_TOKEN}`);
    
    expect(res.status).toBe(200);
    expect(res.body.data).toBeInstanceOf(Array);
    expect(res.body.data.length).toBeGreaterThan(0);
    expect(res.body.data[0]).toMatchObject({
      id: expect.any(String),
      name: expect.any(String),
      email: expect.any(String)
    });
    // 機密情報が含まれていないことを確認
    expect(res.body.data[0].password).toBeUndefined();
  });
  
  it('ページネーションが正しく動作する', async () => {
    const res = await request(app)
      .get('/api/users?page=1&limit=5')
      .set('Authorization', `Bearer ${TEST_TOKEN}`);
    
    expect(res.body.pagination.page).toBe(1);
    expect(res.body.pagination.limit).toBe(5);
    expect(res.body.data.length).toBeLessThanOrEqual(5);
  });
});

POST/PUT/DELETEのテスト

describe('POST /api/users', () => {
  it('新規ユーザーを作成できる', async () => {
    const res = await request(app)
      .post('/api/users')
      .set('Authorization', `Bearer ${ADMIN_TOKEN}`)
      .send({
        name: '新規ユーザー',
        email: 'new@example.com',
        role: 'user'
      });
    
    expect(res.status).toBe(201);
    expect(res.body.id).toBeDefined();
    expect(res.body.email).toBe('new@example.com');
  });
  
  it('既存メールアドレスは409を返す', async () => {
    await request(app)
      .post('/api/users')
      .set('Authorization', `Bearer ${ADMIN_TOKEN}`)
      .send({ name: 'dup', email: 'test@example.com' });
    
    const res = await request(app)
      .post('/api/users')
      .set('Authorization', `Bearer ${ADMIN_TOKEN}`)
      .send({ name: 'dup2', email: 'test@example.com' });
    
    expect(res.status).toBe(409);
  });
});

GitHub ActionsへのCI組み込み

# .github/workflows/api-tests.yml
name: API Tests

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    
    services:
      postgres:
        image: postgres:15
        env:
          POSTGRES_DB: test_db
          POSTGRES_USER: test
          POSTGRES_PASSWORD: test
        ports:
          - 5432:5432
    
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      
      - run: npm ci
      - run: npm run db:migrate:test
        env:
          DATABASE_URL: postgresql://test:test@localhost:5432/test_db
      
      - run: npm test -- --coverage
        env:
          DATABASE_URL: postgresql://test:test@localhost:5432/test_db
          JWT_SECRET: test-secret
      
      - name: カバレッジレポートのアップロード
        uses: actions/upload-artifact@v4
        with:
          name: coverage-report
          path: coverage/

テストカバレッジの目標

  • ユニットテスト:ビジネスロジック・バリデーション(カバレッジ80%以上目標)
  • 統合テスト:全APIエンドポイントの正常系・異常系
  • E2Eテスト:主要なユーザーフロー(認証→操作→検証)

まとめ

JestとSupertestの組み合わせはNode.jsのAPIテストに最適で、実際のHTTPリクエストレベルでの統合テストが書きやすいです。GitHub ActionsにCIを組み込むことで、プルリクエストのたびにAPIテストが自動実行され、品質を担保しながら継続的なリリースが可能になります。

よくある質問

Q.SuptertestはどのHTTPフレームワークと組み合わせられますか?

Supertestはnode-superagentベースのHTTPテストライブラリで、Express・Koa・Fastify・Next.js APIルートなど、Node.jsのHTTPサーバーならほぼ何でも組み合わせられます。

Q.APIテストでデータベースのモックはどうすればよいですか?

テスト用のデータベースを別途用意する方法、JestのモックでDB呼び出しをスタブする方法、インメモリDBを使う方法(SQLiteなど)があります。統合テストにはテスト用DB、ユニットテストにはモックが一般的です。

Q.GitHub ActionsでAPIテストが失敗した場合にデプロイを止めるには?

GitHub Actionsのjobsに依存関係(needs)を設定し、テストジョブが成功した場合のみデプロイジョブが実行されるよう設定します。テストが失敗するとワークフロー全体が停止します。

関連記事