SpecFormula AI
ISA後端指令

ApiCall

以宣告式語法執行 HTTP API 請求

ApiCall 讓你直接在 Gherkin 中宣告 API 呼叫的參數與回應擷取,無需撰寫 HTTP 請求的程式碼。支援 DataTable 和 JSON 兩種格式。

兩種格式

格式適用場景
DataTable扁平欄位,直覺易讀
DocString JSON巢狀結構,貼近實際 Request Body

DataTable 格式

When (UID="$Alice.id") 新增待辦事項, call table:
  | >todo1.id | title  |
  | <todoId   | 買牛奶 |

JSON 格式

When (UID="$Alice.id") 新增待辦事項, call JSON:
  """json
  {
    >todo1.id: <todoId,
    "title": "買牛奶"
  }
  """

兩者效果相同:

  1. $Alice.id 作為呼叫者身份(Bearer Token)
  2. 呼叫 API Spec 中 summary 為「新增待辦事項」的 API
  3. title: 買牛奶 作為 Request Body
  4. 從回應中擷取 todoId 欄位,存入變數 todo1.id

JSON 格式注意事項:符號系統的表達式($變數、&CAS 約束、@時間符號、>/<擷取鍵)不可放在 JSON 字串引號 "" 內,否則會被視為純字串。

{ "title": $todo1.title }    // 正確:符號在引號外
{ "title": "$todo1.title" }  // 錯誤:符號被引號包裹,視為純字串

欄位語法

DataTable 的欄位名稱支援特殊前綴,用於指定參數類型:

前綴用途範例
(無)Request Body 欄位;若欄位名稱同時存在於 API Spec 的 Path/Query/Header 參數中,會自動帶入對應位置(等同同時填寫 P:/Q:/H:titletodoId
P:Path 參數P:todoId → 替換 URL 中的 {todoId}
Q:Query 參數Q:page?page=value
H:Request HeaderH:X-Trace-Id
B:顯式標記整個 Request Body(裸值、陣列、物件)B: → 將 cell 值作為整個 body
>存入變數 — 定義要儲存回應值的變數名稱>todo1.id
<取自回應 — 指定回應 JSON 中的來源欄位<todoId
<H:取自回應 Header — 指定回應 Header 的來源欄位<H:Location
<B:取自回應 Body(裸值) — 擷取整個 response body<B:

JSON 格式的前綴語法

JSON 格式使用大寫字母前綴加引號的語法,對應 DataTable 的 P:/Q:/H: 前綴:

DataTableJSON說明
P:todoIdP"todoId"Path 參數
Q:pageQ"page"Query 參數
H:X-Trace-IdH"X-Trace-Id"Request Header
B:B:顯式標記整個 Body
# DataTable 版本
When (UID="$Alice.id") 更新待辦事項, call table:
  | P:todoId  | Q:verbose | H:X-Trace | title  |
  | $todo1.id | true      | trace-123 | 買豆漿 |

# JSON 版本
When (UID="$Alice.id") 更新待辦事項, call JSON:
  """json
  {
    P"todoId": $todo1.id,
    Q"verbose": "true",
    H"X-Trace": "trace-123",
    "title": "買豆漿"
  }
  """

擷取變數配對

>< 必須在同一欄中上下配對:header 放變數名(>),row 放來源欄位(<)。

| >todo1.id | >todo1.title | title  |
| <todoId   | <title       | 買牛奶 |

執行後:

  • todo1.id = 回應中的 todoId
  • todo1.title = 回應中的 title

擷取 Response Header

使用 <H: 前綴擷取回應中的 HTTP Header 值:

# DataTable — 擷取 Location header
When (No Actor) 新增資源, call table:
  | >loc        | name   |
  | <H:Location | 測試資源 |

# JSON — 擷取 X-Request-Id header
When (No Actor) 新增資源, call JSON:
  """json
  {
    >requestId: <H"X-Request-Id",
    "name": "測試資源"
  }
  """

擷取裸值 Response Body

當 API 回傳裸值(如 true42)而非 JSON 物件時,使用 <B: 擷取整個 response body:

When (No Actor) 查詢資源狀態, call table:
  | >result | P:id  |
  | <B:     | $res1 |

與 API Spec 的對應關係

ApiCall 指令透過 summary 欄位識別要呼叫的 API。這個設計讓測試檔案使用業務語言,而非技術性的 URL 路徑。

對應機制

# api-spec.yml (OpenAPI 3.0)
paths:
  /todos:
    post:
      summary: 新增待辦事項    # ← ApiCall 透過這個 summary 識別
      operationId: createTodo
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - title
              properties:
                title:
                  type: string
                priority:
                  type: integer
      responses:
        "201":
          content:
            application/json:
              schema:
                type: object
                properties:
                  todoId:
                    type: integer
                  title:
                    type: string
# 對應的 Gherkin 語法
When (UID="$Alice.id") 新增待辦事項, call table:
  | title  | priority |
  | 買牛奶 | 1        |

對應表

Gherkin 元素API Spec 來源
新增待辦事項paths.*.*.summary
HTTP Method從 path 定義推斷(post, get, put, delete
URL Pathpaths 的 key(如 /todos
Request Body 欄位requestBody.content.*.schema.properties
Path 參數parameters[].in: path
Query 參數parameters[].in: query
Response 欄位responses.*.content.*.schema.properties

summary 唯一性要求

每個 API 操作的 summary 在整份 API Spec 中必須唯一:

# ✅ 正確:summary 各不相同
/todos:
  post:
    summary: 新增待辦事項
  get:
    summary: 查詢待辦事項列表

/todos/{id}:
  get:
    summary: 查詢單一待辦事項
  put:
    summary: 更新待辦事項
  delete:
    summary: 刪除待辦事項

# ❌ 錯誤:重複的 summary 會導致無法識別
/todos:
  get:
    summary: 查詢待辦事項
/todos/{id}:
  get:
    summary: 查詢待辦事項    # 重複!

自動參數分類

當 Body 欄位名稱與 API Spec 中的 Path/Query/Header 參數同名時,該值會同時作為 Body 欄位送出,也會自動填入同名的參數位置,無需額外加 P:Q: 前綴:

# todoId 同時是 Body 和 Path 參數 → 自動帶入 P:todoId
| todoId | title  |
| 123    | 買豆漿 |

巢狀 JSON 建構

支援 dot notation 和陣列索引,自動建構深層 JSON:

# DataTable — 使用 dot notation 表達巢狀結構
| name   | config.theme | tags[0] | tags[1] |
| 我的專案 | dark        | work    | urgent  |

JSON 格式可直接寫出巢狀結構,更直覺:

# JSON — 直接描述巢狀結構
"""json
{
  "name": "我的專案",
  "config": { "theme": "dark" },
  "tags": ["work", "urgent"]
}
"""

兩者產生相同的 Request Body:

{
  "name": "我的專案",
  "config": { "theme": "dark" },
  "tags": ["work", "urgent"]
}

Key 含 . 的逃脫語法

當 JSON key 本身包含 . 字元時(如 "btn.save"),使用 ["..."] bracket notation 避免被誤拆為多層巢狀:

# DataTable — 使用 bracket notation
| content.["btn.save"] | content.["btn.cancel"] |
| 儲存                  | 取消                    |

JSON DocString 扁平化時會自動處理逃脫,使用者直接撰寫原始 key 即可:

# JSON — key 自然包含 ".",框架自動處理
"""json
{
  "content": {
    "btn.save": "儲存",
    "btn.cancel": "取消"
  }
}
"""

["..."](字串 key 逃脫)與 [0](陣列索引)可共存於同一路徑中:

data.[0].["field.name"]
→ data 為物件 key、[0] 為陣列索引、["field.name"] 為含 "." 的物件 key

頂層陣列 Body

當 API 的 request body 為頂層陣列(如批次建立),JSON DocString 可直接使用 [...] 作為頂層:

When (No Actor) 批次新增待辦事項, call JSON:
  """json
  [
    { "title": "買牛奶", "priority": 1 },
    { "title": "寫報告", "priority": 2 }
  ]
  """

需同時傳遞 Path/Query/Header 參數時,頂層維持 {},以 B: 標記 body 內容:

When (No Actor) 批次新增待辦事項, call JSON:
  """json
  {
    P"projectId": $project1.id,
    Q"notify": "true",
    B: [
      { "title": "買牛奶", "priority": 1 },
      { "title": "寫報告", "priority": 2 }
    ]
  }
  """

等價 DataTable:

| P:projectId  | Q:notify | [0].title | [0].priority | [1].title | [1].priority |
| $project1.id | true     | 買牛奶     | 1            | 寫報告     | 2            |

B: 對物件 Body 也適用,提供「參數與 body 視覺分離」的選項(非前綴 key 的隱式 body 規則維持向後相容):

# 隱式 body(現行寫法,向後相容)
"""json
{ P"projectId": $project1.id, "title": "買牛奶", "priority": 1 }
"""

# 顯式 B: body(等價寫法)
"""json
{
  P"projectId": $project1.id,
  B: { "title": "買牛奶", "priority": 1 }
}
"""

注意B: 與非前綴的一般 key(如 "name")不可共存——使用 B: 時,body 全部收納於 B: 值內。

裸值 Body

當 API 的 request body 接受原始型別(booleanstringinteger),使用 B: 標記裸值:

# DataTable — B: 前綴承載裸值
When 切換資源狀態, call table:
  | P:id  | B:   |
  | $res1 | true |

# JSON — B: 承載裸值
When (No Actor) 切換資源狀態, call JSON:
  """json
  { P"id": $res1, B: true }
  """

When (No Actor) 提交回饋, call JSON:
  """json
  { B: "這是一段回饋" }
  """

呼叫者身份

(UID="...") 指定呼叫者身份,框架會自動注入對應的 Bearer Token:

# 使用變數作為呼叫者
When (UID="$Alice.id") 查詢待辦事項, call table:
  | Q:status |
  | active   |

# 無需身份驗證
When (No Actor) 健康檢查, call table:
  | |

即使沒有參數,仍需提供空的 DataTable 以符合語法要求。

時間格式

ApiCall 中的時間值會根據 isa.yml 設定的 time_format 自動轉換:

格式輸出範例
ISO(預設)"2025-12-25T14:30:00+08:00"
TIMESTAMP1766735400000
EPOCH1766735400
DATE_ONLY"2025-12-25"
TIME_ONLY"14:30:00"

錯誤處理

Lint 階段

錯誤說明
欄位不存在DataTable 中的欄位名稱在 API Spec 找不到定義
缺少必填欄位OpenAPI 標記為 required 的參數未提供(可透過 @allow-missing-parameters 於特定情境放寬)
擷取欄位不存在< 指定的回應欄位在 Response Schema 中找不到

@allow-missing-parameters:允許參數型前置條件

當你的測試目標是「驗證 API 會拒絕缺少/無效的必填參數」時,可在 Example(或 Scenario)加上 @allow-missing-parameters,讓 必填參數檢查 在該情境被跳過,但其他契約檢查仍維持啟用。

Feature: 查詢 API 參數驗證

  Rule: 必填參數驗證規則

    Example: 提供完整參數應該成功
      When (UID="$Alice.id") 查詢資料, call table:
        | Q:startDate | Q:endDate  | Q:pageNo | Q:pageSize |
        | 2024-01-01  | 2024-01-31 | 1        | 10         |
      Then 查詢資料(200)回應, with table:
        | success |
        | true    |

    @allow-missing-parameters
    Example: 缺少必填參數 startDate 應該失敗
      When (UID="$Alice.id") 查詢資料, call table:
        | Q:endDate  | Q:pageNo | Q:pageSize |
        | 2024-01-31 | 1        | 10         |
      Then 查詢資料(400)回應, with table:
        | errorCode                  |
        | MISSING_REQUIRED_PARAMETER |

何時使用

  • 缺少 OpenAPI required 參數(例如少 Q:startDate
  • 必填參數值無效,且你的後端會以「缺少/無效參數」類型錯誤回應

何時不要使用

  • 業務規則驗證失敗(例如查詢區間超過 6 個月)
  • 權限或認證失敗
  • 資料狀態不符(例如重複申請)
  • 參數完整且格式正確,但因業務邏輯被拒絕

執行階段

錯誤說明
API not foundsummary 在 API Spec 中找不到對應操作
重複的 summary多個操作使用相同 summary,無法唯一識別

完整範例

Feature: 待辦事項管理

  Background:
    Given 現在時間為 "2026-01-27T10:00:00"

    # EntitySetup 指令 — 準備測試資料(參見 EntitySetup 文件)
    # >Alice.id / <userId:擷取回應中的 userId 存入 Alice.id 變數
    Given 準備一個使用者, with table:
      | >Alice.id | name  | email             | status |
      | <userId   | Alice | alice@example.com | ACTIVE |

  Example: 新增並更新待辦事項

    # (No Actor):無需身份驗證的 API 呼叫
    # >AliceToken / <token:擷取回應中的 token
    When (No Actor) 使用者登入, call table:
      | >AliceToken | email             | password    |
      | <token      | alice@example.com | password123 |

    # (UID="$Alice.id"):以 Alice 身份呼叫,自動注入 Bearer Token
    When (UID="$Alice.id") 新增待辦事項, call table:
      | >todo1.id | title  |
      | <todoId   | 買牛奶 |

    # P: 前綴表示 Path 參數;$todo1.id 引用先前擷取的變數
    When (UID="$Alice.id") 更新待辦事項, call table:
      | P:todoId  | title  |
      | $todo1.id | 買豆漿 |

    # ResponseValidate 指令 — 驗證回應(參見 ResponseValidate 文件)
    Then 更新待辦事項(200)回應, with table:
      | todoId    | title  | updatedAt                        |
      | $todo1.id | 買豆漿 | &sameTime("2026-01-27T10:00:00") |

目錄