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テストが自動実行され、品質を担保しながら継続的なリリースが可能になります。