# backend 結合テスト

## 関連パス

| 項目 | パス |
| --- | --- |
| テストファイルのディレクトリ | `backend/tests/integration` |
| ローカル環境 データセット仕様 | `database/fixture/integrationtest/README.md` |
| クライアントAPIリファレンス実装 | `backend/tests/integration/api/client/usage-events/post/test_usage_events_post.py` |

## 基本方針

- 同じ入力であれば、外部要因に依存せず同じ結果になること（決定論的）。
- "何を保証するものなのか"が一目でわかること（高い可読性）。
- ケース失敗時の要因が単一であり、原因となるロジックを特定しやすいこと。
- 実装の変更に強く、保守が容易であること。
- 実行速度が高速であること。

## 責務

結合テストでは **APIエンドポイントの契約が成立すること** を検証する。内部ロジックの分岐網羅や外部依存への値伝播の詳細は単体テストに委ねる。

| 観点 | 結合テスト | 単体テスト |
| --- | --- | --- |
| API契約 | ステータスコード、リクエストバリデーション、代表的なエラーメッセージ | — |
| 外部依存 | モック差し替え後のエンドポイント成立 | 呼び出し引数・値伝播の詳細 |
| 内部ロジック | — | 分岐網羅・副作用の検証 |

## 仕様

### 認証

- 認証系の網羅的なケース（APIキー欠損・空文字・不正フォーマット・不正シークレット等）は ClientAPIは`test_quota_check_head.py` に集約する。
- **他のAPIテストでは認証系のテストを実施せず**、 エンドポイント固有のバリデーション（冪等キー・リクエストボディ等）のみテストする。
- ManagementAPIのエンドポイント個別テストでは、認証済みテナントを fixture で差し替え、Bearer JWT の検証自体は認証の単体テストに委ねる。

### 外部依存のモック

| 依存先 | 方針 |
| --- | --- |
| DB（PostgreSQL） | **モックしない。** docker-compose で起動したDBを使用する |
| AWSサービス（S3, SQS 等） | `monkeypatch.setattr` でルーターモジュール上の依存を差し替える |

- 差し替えは `MagicMock(spec_set=<依存クラス>)` を必須とし、存在しない属性アクセスを検知可能にする。
- 差し替えは対象API種別の `conftest.py` の fixture として定義し、**必要なテストクラスだけ `@pytest.mark.usefixtures(...)` で明示適用** する。
- 差し替えモックは契約成立に必要な最小責務に限定し、状態管理用の独自APIを追加しない。
- 外部依存の設定値、および依存差し替えは対象API種別の `conftest.py` の fixture で `monkeypatch` により適用する。
- キーワード引数の命名はプロダクションコードと合わせる。

## コーディングスタイル

常に`リファレンス実装`を参考にすること。

### ファイル・クラス構成

- APIの種類、パス、メソッドの順にディレクトリを作成する。（例: `backend/tests/integration/api/client/quota/check/head`）
- テスト対象の **APIエンドポイントのパス × HTTPメソッド** ごとに1テストクラスを作成する。
- 原則 **1ファイル = 1クラス** とする。
- テスト関数は必ずクラス内に定義する（モジュールレベル関数は禁止）。

### 命名規則

原則、PEP8に準拠する。

#### テストクラス名

`Test<APIパス><メソッド>` とする。

#### テスト関数名

`test_<条件>` または `test_<条件>_<補足>` とする。

クラス文脈と assert で判別できるため、**関数名にHTTPメソッド・エンドポイント名・ステータスコードを含めない**

```python
class TestQuotaCheckHead:
    def test_api_key_missing(self, test_client: TestClient) -> None: ...
    def test_quota_available_for_another_client_tenant(self, test_client: TestClient) -> None: ...
```

### テスト関数の順序

正常系を先、エラー系を後に配置する。エラー系内の順序も以下に従う。

1. 正常系テスト
2. 認証系テスト
3. バリデーション系テスト
4. 業務系テスト

### テスト関数仕様

- AAA（Arrange-Act-Assert）を遵守する。
- 正常系・異常系の双方で、欠損だけでなく空文字のような境界値も含めて契約を検証する。

### 定数定義

| データの種類 | 配置場所 |
| --- | --- |
| DB登録済みの識別子（有効APIキー、テナント名、service名、feature名、価格コード等） | 対象API種別の `conftest.py` の fixture |
| ヘッダ名（`X-API-Key` など）などの広く使われるアプリケーション固有の値 | 対象API種別の `conftest.py` の fixture |
| 異常系データ（不正APIキー、不正形式、存在しないテナント名等）・単純リテラル（数量・任意文字列） | テスト関数内に直接記述 |

- **各テストファイルにモジュールレベル定数を定義しない。**

### アサーション

| HTTPメソッド | 検証内容 |
| --- | --- |
| HEAD | ステータスコードのみ |
| POST / GET 等 | レスポンスボディを検証する。JSONレスポンスの場合は `response.json()` の内容を検証し、空ボディ契約の場合は空であることを検証する |
| エラーレスポンス | `detail` が業務的に意味のあるメッセージの場合に検証する |

```python
assert response.status_code == status.HTTP_400_BAD_REQUEST
assert response.json()["detail"] == "Invalid code."
```

### conftest.py の責務

親ディレクトリの `conftest.py` は、配下のテスト種別に共通する最小限の fixture だけを責務とする。

1. **環境変数の設定**
2. **共通インフラ fixture の提供**

API種別に固有の fixture は、対象API種別の `conftest.py` に配置する。

1. **`test_client` fixture の提供**
2. **API固有データの fixture の提供**（「テストデータの配置基準」参照）
3. **外部依存差し替え用 fixture の提供**（`monkeypatch` による設定値・クライアント差し替え）

### 禁止パターン

- 可読性を妨げる過剰な共通化は禁止。
- `_post`, `_get`, `_headers` のような **薄いラッパー関数** は禁止。
- 関数名の情報が失われるため、`pytest.parametrize` は禁止。

## 検証

コードの実装後は以下の検証を必ず行い、発生した問題を修正すること。

- テスト実行: `pytest backend/tests/integration`
- Lint Check: `uv run ruff check backend/tests/integration`
