iT邦幫忙

2026 iThome 鐵人賽

DAY 20
1
Software Development

從脆弱腳本到可信任測試平台:自動化測試架構30天系列 第 20

Day 20|如何設計有價值的API自動化測試:除了 200 OK 以外的關鍵驗證

  • 分享至 

  • xImage
  •  

「如果你的 API 自動化測試只檢查 response.status_code == 200,那它就跟一個只會回傳『我還活著』但內容全錯的無人客服沒兩樣。」

大家好,我是 Jane,一個每天在第一線處理跨平台(Web & App)自動化測試、跟 CI/CD Pipeline 與 API 架構奮戰的自動化測試工程師(SDET)。

在上集 Day 19 中,我們剖析了為什麼 API 測試比 UI 測試划算 10 倍,並確立了「將邏輯驗證下沉至 API 層」的策略。

然而,當我開始進行 API 測試 Code Review 時,最常看到的「假安全感」程式碼就是:

Python

response = requests.get("https://api.example.com/user/123")
assert response.status_code == 200  # 寫完,Pass!

這種測試非常危險!因為在真實世界中,API 回傳 200 OK 的同時,可能藏著無數致命缺陷:

  • JSON 回傳了 200 OK,但 data 欄位是 null
  • 後端欄位改名(例如 userId 變成 user_id),導致 Web 與 App 靜默崩潰(Silent Failure)。
  • 扣款 API 被重複呼叫第二次時,依然回傳 200 OK 並重複扣款(缺乏冪等性 Idempotency)。

今天這篇文章,我們就來好好聊聊:一套真正有價值的 API 自動化測試,除了 200 OK 之外,還應該包含哪 7 大關鍵驗證?

一、 有價值的 API 測試:7 大深層驗證維度

                    ┌─────────────────────────┐
                    │  API 深度驗證 7 大維度    │
                    └────────────┬────────────┘
                                 │
     ┌───────────────────────────┼───────────────────────────┐
     ▼                           ▼                           ▼
【結構與內容】                 【商業與安全性】              【狀態與機制】
1. Response Schema 結構驗證  4. 權限與越權檢驗 (BAC)      6. 狀態變化驗證 (DB/State)
2. 關鍵欄位型態與數值        5. 邊界值與異常 Error Handling 7. 冪等性驗證 (Idempotency)
3. 業務 logic 斷言 (Business)

1. Response Schema(資料結構與契約)

驗證 JSON 的 Key 名稱、資料型態(String, Int, Array)、是否允許 Null,以及必填欄位是否存在。這是防止前端 Web/App 閃退的第一道防線。

2. 關鍵欄位內容與數值計算(Field Value Check)

針對計算型欄位(如:折扣後總金額、稅金、運費計算),驗證其演算法結果是否精準無誤。

3. 業務 logic 斷言(Business Rule Verification)

驗證 API 的商業限制,例如:「庫存為 0 的商品無法建立訂單」、「過期優惠券無法折抵」。

4. 權限與越權檢驗(Authorization & BAC)

  • 未帶 Token 請求應回傳 401 Unauthorized
  • 一般 User 嘗試呼叫 Admin API 應回傳 403 Forbidden
  • 驗證水平越權(B 帳號不能查詢/修改 A 帳號的訂單資料)。

5. 邊界值與異常 Error Handling

輸入超長字串、Null、SQL Injection 常用字元或負數,驗證 API 能回傳結構統一且友善的錯誤 JSON(如 400 Bad Request),而不是崩潰噴出 500 Internal Server Error 與StackTrace。

6. 狀態變化驗證(Side Effects / State Change)

發送 POST/PUT/DELETE 請求後,除了檢查當下 Response,還要發送 GET 請求或查詢 DB,確認資料狀態確實被更新了

7. 冪等性驗證(Idempotency Check)

對於轉帳、付款或建立訂單等 API,連續發送 2 次帶有相同 Idempotency-Key 的請求,第二次呼叫應被攔截或回傳相同結果,絕不能造成重複扣款或重複建立資料

二、 Python / pytest 實戰:利用 Pydantic 進行 Schema 嚴格驗證

在 Python 生態系中,強烈推薦結合 pydantic 套件來做 API Response Schema 驗證。它比手動寫十幾個 assert key in data 快且精準 10 倍!

1. 定義 API 契約模型 (schemas/user_schema.py)

Python

# schemas/user_schema.py
from pydantic import BaseModel, EmailStr, Field
from typing import Optional, List

class UserProfileSchema(BaseModel):
    id: str
    username: str
    email: EmailStr  # 自動驗證 Email 格式
    role: str = Field(..., pattern="^(admin|regular|vip)$") # 限制 Enum 值
    is_active: bool
    phone: Optional[str] = None  # 允許為 None 或 String

class UserListResponseSchema(BaseModel):
    total: int = Field(ge=0) # 必須大於等於 0
    users: List[UserProfileSchema]

2. 在 pytest 中進行深度 API 驗證 (tests/api/test_user_api.py)

結合 Schema 驗證、狀態碼、權限與商業邏輯的完整 API 測試案例:

Python

# tests/api/test_user_api.py
import pytest
import requests
from pydantic import ValidationError
from schemas.user_schema import UserListResponseSchema
from config.settings import settings

def test_get_user_list_schema_and_business_rules(auth_headers: dict):
    """
    測試情境:驗證使用者列表 API 的 Schema 結構、資料型態與內容 logic
    """
    url = f"{settings.BASE_URL}/api/v1/users?page=1&limit=10"
    response = requests.get(url, headers=auth_headers)

    # 1. 基礎狀態碼斷言
    assert response.status_code == 200

    # 2. 深度 Schema 驗證 (使用 Pydantic)
    # 如果後端改了欄位名稱或型態錯誤,ValidationError 會立刻印出精確的欄位差錯!
    try:
        validated_data = UserListResponseSchema(**response.json())
    except ValidationError as e:
        pytest.fail(f"API Response Schema 驗證失敗:\n{e}")

    # 3. 業務 Logic 斷言
    assert validated_data.total >= len(validated_data.users)
    assert len(validated_data.users) <= 10  # 驗證分頁 limit=10 生效

def test_payment_idempotency(auth_headers: dict):
    """
    測試情境:驗證付款 API 的冪等性 (Idempotency)
    """
    url = f"{settings.BASE_URL}/api/v1/payments"
    idempotency_key = "test_key_uuid_998123"

    headers = {**auth_headers, "X-Idempotency-Key": idempotency_key}
    payload = {"order_id": "ORD_881", "amount": 500}

    # 第一次呼叫:應該成功扣款
    res1 = requests.post(url, json=payload, headers=headers)
    assert res1.status_code == 201
    tx_id_1 = res1.json()["transaction_id"]

    # 第二次呼叫:帶相同的 Idempotency-Key,應該回傳相同交易結果或被攔截
    res2 = requests.post(url, json=payload, headers=headers)
    assert res2.status_code in (200, 201)
    tx_id_2 = res2.json()["transaction_id"]

    # 驗證第二次並沒有產生全新的交易單號 (防止重複扣款)
    assert tx_id_1 == tx_id_2

三、 第一線 SDET 的心法總結

  1. 引入 OpenAPI / Swagger JSON Schema 自動化比對
    如果開發團隊有維護 OpenAPI (Swagger),可以在 CI 中寫一個 Helper,將 API 實際 Response 與 Swagger 的 Schema 進行自動化 Diff。Schema 稍微不合立刻警報!
  2. 負面測試(Negative Testing)與正面測試同樣重要
    好的 API 測試,至少有一半的案例在測「各種不合規的請求」。驗證 API 是否能優雅地拒絕非法資料,是系統防禦力(Robustness)的關鍵指標。
  3. API 測試是 CI Pipeline 的最高效門檻
    在 CI Pipeline 中,將這套高價值、執行速度快的 API 測試放在 Commit / PR 階段執行。80% 的邏輯錯誤在 1 分鐘內就能被攔截,根本不需要等到昂貴的 UI 測試登場!

結語與第四部分總結

恭喜你!到今天 Day 20 為止,我們完成了《第四部分:測試資料、環境與 API 整合》。

我們解決了:

  • Day 16:測試資料依賴與污染陷阱
  • Day 17:Fixture + Data Factory 動態建立與 Teardown 清理
  • Day 18:外部服務 Mock / Stub 與抉擇矩陣
  • Day 19:API 測試 vs UI 測試的效益與下沉策略
  • Day 20:超越 200 OK 的深層 API 測試設計

從明天開始,我們將邁入整條自動化流水線的最關鍵一環:
《第五部分:讓測試真正進入 CI/CD》

明天 Day 21,我們將手把手討論:
《 Day 21|測試在 Pipeline 裡應該放在哪裡?建構高效的 CI/CD 測試階段 》


上一篇
Day 19|為什麼API測試通常比UI測試更划算?效益與維護成本深度剖析
下一篇
Day 21|測試在Pipeline裡應該放在哪裡?建構高效的 CI/CD 測試階段
系列文
從脆弱腳本到可信任測試平台:自動化測試架構30天23
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言