1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Schemathesis: APIの自動テストツール完全ガイド

Posted at

はじめに

API開発において、仕様書との整合性確認やエッジケースのテストは重要ですが、手動で網羅的にテストするのは困難です。そして、Schemathesisは、OpenAPIやGraphQLの仕様書から自動的にテストケースを生成し、APIの堅牢性を検証できるPythonベースのテストツールです。

本記事では、インターン生の私が実際にSchemathesisを使用して理解した内容や、公式GitHubおよびドキュメントに記述されている内容をもとに、基本的な使い方から実践的な活用方法まで解説します。

Schemathesisとは

SchemathesisはProperty-Based Testing(プロパティベーステスト)の手法を用いて、API仕様書から自動的にテストケースを生成するツールです。

主な特徴

  • 自動テスト生成: OpenAPI/GraphQL仕様からテストケースを自動生成
  • プロパティベーステスト: 想定外のエッジケースを発見
  • 包括的なカバレッジ: すべてのエンドポイント、パラメータ、メソッドを網羅的にテスト
  • 高速実行: 並列実行による効率的なテスト
  • CI/CD統合: GitHub ActionsやJenkinsなどと簡単に統合可能
  • 詳細なレポート: 失敗したケースの再現方法を含むレポート生成
  • リグレッションテスト: API変更による影響を迅速に検出

Schemathesisで検出できる問題の種類

1. HTTPステータスコードの不整合

OpenAPI仕様書に定義されていないHTTPステータスコードが返される場合に検出されます。

検出例:

❌ Undocumented HTTP status code
    Received: 405
    Documented: 200, 401, 403, 500

原因:

  • 仕様書の記述漏れ
  • 実装と仕様の不一致
  • エラーハンドリングの不備

2. 必須ヘッダーの検証不足

必須として定義されているヘッダー(例: Authorization)が欠落している場合、適切なエラー(401)を返さない問題を検出します。

検出例:

❌ Missing header not rejected
    Got 500 when missing required 'Authorization' header, expected 401

原因:

  • 認証ミドルウェアの未初期化
  • 例外ハンドリングの不備
  • 依存関係の注入エラー

3. サポートされていないHTTPメソッド

OpenAPI仕様書で定義されていないHTTPメソッドに対して、405 Method Not Allowedを返さない問題を検出します。

検出例:

❌ Unsupported methods
    Unsupported method PUT returned 401, expected 405 Method Not Allowed

原因:

  • ルーティングレイヤーより先に認証が実行される
  • FastAPI/Expressなどのフレームワークのデフォルト動作
  • ミドルウェアの実行順序の問題

4. スキーマ検証の不一致

OpenAPI仕様で定義されたバリデーションルールと、実際のAPI実装のバリデーションが一致しない場合に検出されます。

検出例:

❌ Schema validation mismatch
    51 operations mostly rejected generated data due to validation errors

原因:

  • 仕様書の制約が実装より緩い
  • 実装側に追加のバリデーションロジックが存在
  • 暗黙的なビジネスルールが仕様書に記載されていない

5. サーバーエラー (5xx系)

予期しないサーバーエラーが発生する場合を検出します。

検出例:

❌ Server error
    [500] Internal Server Error

原因:

  • 未初期化のリソース(データベース接続など)
  • Null参照エラー
  • 例外の未処理

6. 無効なデータに対する受理

スキーマに準拠したデータがAPIによって拒否される、または準拠していないデータが受理される問題を検出します。

検出例:

❌ API rejected schema-compliant request
    Valid data should have been accepted
    Expected: 2xx, 401, 403, 404, 5xx

Schemathesisのテストフェーズ

1. Examples(サンプルテスト)

OpenAPI仕様書に記載されているexamplesを使用してテストを実行します。

components:
  schemas:
    User:
      type: object
      properties:
        name:
          type: string
          example: "John Doe"
        age:
          type: integer
          example: 30

2. Coverage(カバレッジテスト)

すべてのエンドポイント、メソッド、パラメータの組み合わせを網羅的にテストします。

  • 各HTTPメソッドの検証
  • パスパラメータの組み合わせ
  • クエリパラメータの組み合わせ
  • リクエストボディの検証

3. Fuzzing(ファジングテスト)

ランダムかつ予測不可能なデータを生成してAPIの堅牢性をテストします。

  • 境界値のテスト
  • 型違反のテスト
  • エッジケースの検出
  • 予期しない入力の処理

4. Stateful Testing(ステートフルテスト)

複数のAPIエンドポイントを連携させて、状態を持つシナリオをテストします。

例:

  1. ユーザー登録 (POST /users)
  2. ログイン (POST /auth/login)
  3. リソース作成 (POST /resources)
  4. リソース取得 (GET /resources/{id})

インストール

pipでインストールできます:

pip install schemathesis

基本的な使い方

1. CLIでの実行

最もシンプルな使い方は、コマンドラインから直接実行する方法です:

# ローカルのOpenAPI仕様ファイルをテスト
schemathesis run openapi.json --base-url=http://localhost:8000

# リモートのAPIをテスト
schemathesis run https://api.example.com/openapi.json

# 認証ヘッダーを追加
schemathesis run ./openapi.json \
  --base-url=http://localhost:8000 \
  --header "Authorization: Bearer YOUR_TOKEN"

# 特定のエンドポイントのみテスト
schemathesis run ./openapi.json \
  --base-url=http://localhost:8000 \
  --endpoint="/api/users"

# ワーカー数を指定して並列実行
schemathesis run ./openapi.json \
  --base-url=http://localhost:8000 \
  --workers=4

2. Pythonコードでの実行

pytest統合を使用して、より柔軟なテストが可能です:

import schemathesis

# OpenAPI仕様からスキーマをロード
schema = schemathesis.from_uri("https://api.example.com/openapi.json")

@schema.parametrize()
def test_api(case):
    # テストケースを実行
    response = case.call()
    # ステータスコードと応答の妥当性を検証
    case.validate_response(response)

3. カスタムチェックの追加

独自の検証ロジックを追加できます:

import schemathesis

schema = schemathesis.from_uri("https://api.example.com/openapi.json")

@schema.parametrize()
def test_api(case):
    response = case.call()
    
    # 標準検証
    case.validate_response(response)
    
    # カスタムチェック
    if response.status_code == 200:
        assert response.headers.get("Content-Type") == "application/json"
        assert "data" in response.json()

高度な使い方

テスト対象のフィルタリング

特定のエンドポイントやメソッドに絞ってテストできます:

# 特定のエンドポイントのみテスト
schemathesis run openapi.json \
  --base-url=http://localhost:8000 \
  --endpoint="/users/*"

# 特定のメソッドのみテスト
schemathesis run openapi.json \
  --base-url=http://localhost:8000 \
  --method=POST

Pythonコードでは:

import schemathesis

schema = schemathesis.from_uri("https://api.example.com/openapi.json")

@schema.parametrize(endpoint="/api/users")
@schema.parametrize(method="POST")
def test_user_creation(case):
    response = case.call()
    case.validate_response(response)

認証の設定

APIキーやBearerトークンを使用した認証にも対応:

# APIキー認証
schemathesis run openapi.json \
  --base-url=http://localhost:8000 \
  --header="X-API-Key: your-api-key"

# Bearer認証
schemathesis run openapi.json \
  --base-url=http://localhost:8000 \
  --header="Authorization: Bearer your-token"

Pythonコードでは:

import schemathesis

schema = schemathesis.from_uri(
    "https://api.example.com/openapi.json",
    headers={"Authorization": "Bearer your-token"}
)

@schema.parametrize()
def test_api(case):
    response = case.call()
    case.validate_response(response)

テストケース数の調整

生成されるテストケース数を制御できます:

# 各エンドポイントに対して100ケース生成
schemathesis run openapi.json \
  --base-url=http://localhost:8000 \
  --hypothesis-max-examples=100

並列実行

複数のワーカーで並列実行することで、テスト時間を短縮:

schemathesis run openapi.json \
  --base-url=http://localhost:8000 \
  --workers=4

テスト結果の解釈

成功例

✅ Coverage (in 88.85s)
    ✅ 52 passed  ❌ 11 failed

✅ Fuzzing (in 176.71s)
    ✅ 62 passed  ❌ 1 failed

失敗の種類別サマリー

Failures:
  ❌ Server error: 1
  ❌ Missing header not rejected: 1
  ❌ Unsupported methods: 10

Warnings:
  ⚠️ Missing authentication: 61 operations returned only 401/403
  ⚠️ Schema validation mismatch: 51 operations rejected data

CI/CDへの統合

GitHub Actionsの例

name: API Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      
      - name: Set up Python
        uses: actions/setup-python@v2
        with:
          python-version: '3.9'
      
      - name: Install dependencies
        run: |
          pip install schemathesis
      
      - name: Start API server
        run: |
          docker-compose up -d
          sleep 10
      
      - name: Run Schemathesis tests
        run: |
          schemathesis run ./openapi.json \
            --base-url=http://localhost:8000 \
            --workers=4 \
            --checks all

実践的なTips

1. 失敗したケースの再現

Schemathesisは失敗したテストケースを再現可能な形で出力します:

schemathesis run openapi.json \
  --base-url=http://localhost:8000 \
  --show-errors-tracebacks

2. 特定のチェックを無効化

必要に応じて特定のチェックをスキップできます:

schemathesis run openapi.json \
  --base-url=http://localhost:8000 \
  --exclude-checks=status_code_conformance

3. テストの除外設定

特定のエンドポイントやケースを除外したい場合:

import schemathesis

schema = schemathesis.from_uri("./openapi.json")

# 特定のエンドポイントを除外
@schema.parametrize(endpoint="^(?!/admin).*")
def test_api(case):
    response = case.call()
    case.validate_response(response)

トラブルシューティング

認証エラーが多発する場合

問題: すべてのテストで401エラーが返される

解決策:

  • 有効な認証トークンを使用しているか確認
  • トークンの有効期限を確認
  • --header オプションで正しくヘッダーを設定

タイムアウトエラーが発生する場合

問題: テストが途中でタイムアウトする

解決策:

# タイムアウト時間を延長
schemathesis run ./openapi.json \
  --base-url=http://localhost:8000 \
  --request-timeout=30000

大量のエラーが発生する場合

問題: 数百件のエラーが報告される

解決策:

  1. まず仕様書と実装の基本的な部分を確認
  2. 段階的にテスト範囲を拡大
  3. 特定のエンドポイントから始める
# 1つのエンドポイントのみテスト
schemathesis run ./openapi.json \
  --base-url=http://localhost:8000 \
  --endpoint="/api/health"

Schemathesisの利点

1. 開発効率の向上

  • 手動テストケース作成の削減
  • リグレッションテストの自動化
  • 仕様変更の影響を即座に検出

2. 品質の向上

  • エッジケースの自動検出
  • 仕様と実装の整合性確保
  • セキュリティ脆弱性の早期発見

3. ドキュメント駆動開発

  • OpenAPI仕様書が単一の信頼できる情報源に
  • フロントエンドとバックエンドの契約が明確に
  • チーム間のコミュニケーション向上

まとめ

Schemathesisを使用することで、APIテストの自動化と網羅性を大幅に向上させることができます。以下のような開発フローで活用できます:

  1. 設計フェーズ: OpenAPI仕様書を作成
  2. 開発フェーズ: Schemathesisでテストを実行しながら実装
  3. テストフェーズ: 包括的なテストで品質を確保
  4. 運用フェーズ: CI/CDパイプラインで継続的にテスト実行

プロパティベーステストの手法により、開発者が想定していないエッジケースを発見できるため、より堅牢なAPIを構築することができます。まずは小規模なプロジェクトで試してみて、徐々に本格的な導入を検討してみてはいかがでしょうか。

参考リンク

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?