API開発において、テスト設計の漏れは本番環境での予期せぬシステムダウンやセキュリティホールの原因になります。本記事では、実務でそのまま使える「正常系・異常系・境界値」のテスト設計チェックリストと、具体的なテストコード例(JavaScript/Jest)を提供します。
読者が抱える課題
- APIテストをどこまで実施すれば品質が担保できるのか基準が曖昧
- 正常系のテストだけで終わってしまい、異常系や境界値の考慮が漏れてバグが発生する
- テスト設計書やチェックリストのテンプレートがなく、開発者ごとにテスト品質にばらつきがある
この記事で分かること
- APIテストで網羅すべき検証項目(正常系・異常系・境界値)のチェックリスト
- HTTPステータスコードとレスポンス構造の設計基準
- Jestを用いたAPIテストコードの実装例
- APIテスト実施時の注意点とよくある失敗パターン
対象読者・前提条件
- REST APIの開発・テストを担当するエンジニア
- 基本的なHTTPプロトコル(メソッド、ステータスコード、ヘッダー)の知識があること
APIテスト検証項目チェックリスト
実務でAPIのテスト設計を行う際に活用できるチェックリストです。スプレッドシートやタスク管理ツールにコピーしてご利用ください。
1. 正常系テスト(Happy Path)
正常なリクエストを送信した際に、期待通りのリソース操作とレスポンスが行われるかを検証します。
| 検証カテゴリ | テスト項目 | 期待される結果 | 優先度 |
|---|---|---|---|
| ステータスコード | 正しいHTTPステータスコードが返却されるか | GET/PUT: 200 OKPOST: 201 CreatedDELETE: 204 No Content など |
高 |
| レスポンスボディ | 必要なプロパティがすべて含まれているか | スキーマ定義通りのキーとデータ型であること | 高 |
| レスポンスボディ | 値の正確性 | データベースに保存された値や計算結果が正確に反映されていること | 高 |
| レスポンスヘッダー |
Content-Type などのヘッダーが適切か |
application/json; charset=utf-8 など |
中 |
| データ永続化 | データベース等の状態変化 | 登録・更新・削除処理がデータベースに正しく反映されていること | 高 |
2. 境界値テスト(Boundary Value)
入力値の制限値(最大値、最小値、文字数、配列の要素数など)の境界を検証します。
| 検証カテゴリ | テスト項目 | 期待される結果 | 優先度 |
|---|---|---|---|
| 数値の境界 | 最小値・最大値の境界値(例: 1〜100の場合、0, 1, 100, 101) | 1, 100は 200 または 2010, 101は 400 Bad Request
|
高 |
| 文字列長 | 最小文字数・最大文字数の境界値 | 境界値内の長さは成功、範囲外は 400 Bad Request
|
高 |
| 配列・リスト | 要素数の上限・下限 | 空配列の許容有無、最大件数制限の挙動が仕様通りであること | 中 |
| 日付・時刻 | 過去・未来・現在時刻の境界 | 予約システム等で、過去日付の指定が拒否されること | 中 |
3. 異常系テスト(Edge Cases & Error Handling)
不正なリクエストやシステムエラーに対して、APIが適切にエラーをハンドリングできるかを検証します。
| 検証カテゴリ | テスト項目 | 期待される結果 | 優先度 |
|---|---|---|---|
| 認証・認可 | トークン未設定、期限切れ、無効なトークン | 401 Unauthorized |
高 |
| 認証・認可 | 権限のないリソースへのアクセス | 403 Forbidden |
高 |
| 存在しないリソース | 無効なIDやパスでのアクセス | 404 Not Found |
高 |
| パラメータ不足 | 必須パラメータが欠落している |
400 Bad Request とエラーメッセージ |
高 |
| 型不整合 | 数値型に文字列を送信、JSON形式の破損 | 400 Bad Request |
高 |
| メソッド不整合 | サポート外のHTTPメソッド(例: GET専用にPOST) | 405 Method Not Allowed |
中 |
| 流量制限 | 短時間での大量リクエスト(レートリミット) | 429 Too Many Requests |
中 |
| サーバーエラー | 内部処理エラー、DB接続タイムアウト |
500 Internal Server Error(詳細なスタックトレースは秘匿する) |
高 |
テストコード実装例
以下は、Node.jsのテストフレームワーク「Jest」とHTTPクライアント「supertest」を使用した、ユーザー登録API(POST /api/v1/users)のテストコード例です。
前提とするAPI仕様
-
エンドポイント:
POST /api/v1/users -
リクエストボディ:
-
username: 文字列(3文字以上20文字以下、必須) -
age: 数値(18以上120以下、必須)
-
テストコード(user.test.js)
const request = require('supertest');
// テスト対象のExpressアプリ(環境に合わせてパスを変更してください)
const app = require('../app');
describe('POST /api/v1/users', () => {
// 1. 正常系のテスト
describe('正常系検証', () => {
it('有効なパラメータを送信した場合、ステータス201と作成されたユーザー情報を返すこと', async () => {
const res = await request(app)
.post('/api/v1/users')
.send({
username: 'testuser',
age: 25
});
expect(res.statusCode).toBe(201);
expect(res.body).toHaveProperty('id');
expect(res.body.username).toBe('testuser');
expect(res.body.age).toBe(25);
});
});
// 2. 境界値のテスト
describe('境界値検証', () => {
it('usernameが下限値(3文字)の場合、登録に成功すること', async () => {
const res = await request(app)
.post('/api/v1/users')
.send({ username: 'abc', age: 30 });
expect(res.statusCode).toBe(201);
});
it('usernameが下限値未満(2文字)の場合、400エラーを返すこと', async () => {
const res = await request(app)
.post('/api/v1/users')
.send({ username: 'ab', age: 30 });
expect(res.statusCode).toBe(400);
});
it('ageが下限値(18)の場合、登録に成功すること', async () => {
const res = await request(app)
.post('/api/v1/users')
.send({ username: 'testuser', age: 18 });
expect(res.statusCode).toBe(201);
});
it('ageが下限値未満(17)の場合、400エラーを返すこと', async () => {
const res = await request(app)
.post('/api/v1/users')
.send({ username: 'testuser', age: 17 });
expect(res.statusCode).toBe(400);
});
});
// 3. 異常系のテスト
describe('異常系検証', () => {
it('必須パラメータ(username)が欠落している場合、400エラーを返すこと', async () => {
const res = await request(app)
.post('/api/v1/users')
.send({ age: 30 });
expect(res.statusCode).toBe(400);
expect(res.body).toHaveProperty('error');
expect(res.body.error).toContain('usernameは必須です');
});
it('データ型が異なる場合(ageに文字列を送信)、400エラーを返すこと', async () => {
const res = await request(app)
.post('/api/v1/users')
.send({ username: 'testuser', age: 'twenty' });
expect(res.statusCode).toBe(400);
});
});
});
※上記のコードはJestおよびsupertestがインストールされた環境で動作します。プロジェクトの構成に合わせてインポートパスやアサーションを調整してください。
実務における注意点とよくある失敗
1. 異常系エラーレスポンスの統一性欠如
エラー時のレスポンスフォーマットがAPIごとに異なると、クライアント側(フロントエンドや外部システム)でのエラーハンドリングが複雑化します。プロジェクト全体でエラーレスポンスの共通スキーマを定義してください。
望ましいエラーレスポンス例:
{
"code": "VALIDATION_ERROR",
"message": "入力内容に不備があります。",
"details": [
{
"field": "age",
"message": "ageは18以上で入力してください。"
}
]
}
2. データベース状態のクリーンアップ漏れ
テストを実行するたびにデータベースにレコードが蓄積され、2回目以降のテストがユニーク制約などで失敗するケースがあります。テスト実行前(beforeEach)または実行後(afterEach)に、テストデータを初期化・ロールバックする仕組みを必ず導入してください。
3. 外部API依存のテスト
テスト対象のAPIが外部のサードパーティAPI(決済ゲートウェイや地図サービスなど)に依存している場合、テスト実行のたびに実APIを呼び出すと、コストの発生やテストの不安定化(ネットワーク遅延によるタイムアウト)を招きます。外部依存部分はモック(Mock)化してテストを実施してください。
まとめ
APIテストは、システムの信頼性を担保するための重要なプロセスです。正常系だけでなく、境界値や異常系のテストケースを設計段階から網羅することで、リリース後の不具合を未然に防ぐことができます。本記事のチェックリストと実装例を参考に、プロジェクトの要件に合わせたテスト設計を行ってください。