實例化規格 (Specification by Example) 目錄
本目錄包含所有 SBE 層級的詳細測試場景,按 Epic 和 User Story 分組。
什麼是 SBE?
實例化規格 (Specification by Example) 是一種協作方法,使用具體的範例來說明系統應如何運作。
SBE 的核心價值
- ✅ 消除歧義 - 用具體數據取代抽象描述
- ✅ 可執行文檔 - 範例可直接轉為自動化測試
- ✅ 共同理解 - 產品、開發、測試對需求有一致認知
- ✅ 回歸測試 - 範例成為長期的驗證基準
SBE 場景索引
Epic 2: 訂單管理
- US-201: 創建訂單
(其他 Epic 與 User Stories 待補充)
SBE 文檔模板
創建新的 SBE 場景文檔時,請使用以下模板:
# Scenario N: {場景名稱}
> **User Story**: [US-XXX: 標題](../../user-stories/epic-N/US-XXX-title.md)
## Given: 系統初始狀態
### 已登入用戶
\`\`\`json
{
"userId": "user-001",
"role": "ROLE_NAME",
"tenantId": "tenant-abc",
"name": "使用者姓名"
}
\`\`\`
### 現有資料
(描述資料庫中的預存數據,使用 JSON 格式)
#### 客戶資料
\`\`\`json
{
"customerId": "cust-123",
"name": "客戶名稱",
"type": "INDIVIDUAL",
...
}
\`\`\`
#### 商品資料
\`\`\`json
[
{
"productId": "prod-001",
"name": "商品名稱",
"basePrice": 1200,
...
}
]
\`\`\`
## When: 執行操作
### API 請求
\`\`\`http
POST /api/v1/{resource}
Authorization: Bearer {token}
Content-Type: application/json
{
"field1": "value1",
"field2": "value2",
...
}
\`\`\`
### UI 操作(可選)
(如需說明 UI 操作流程)
1. 使用者點擊「XXX」按鈕
2. 填寫表單欄位 A、B、C
3. 點擊「提交」按鈕
## Then: 預期結果
### 系統響應
\`\`\`json
{
"id": "resource-001",
"status": "SUCCESS",
"data": {
...
}
}
\`\`\`
**HTTP 狀態碼**: `201 Created`
### 數據庫變更
- `table_name` 表新增 1 筆記錄
- `related_table` 表新增 N 筆記錄
- `audit_logs` 表記錄操作日誌
#### 新增的記錄範例
\`\`\`json
{
"id": "resource-001",
"tenantId": "tenant-abc",
"createdBy": "user-001",
"createdAt": "2025-10-31T10:30:00Z",
...
}
\`\`\`
### 副作用
(系統的其他行為)
- 發送通知給 XXX 角色
- 觸發 YYY 事件
- 更新 ZZZ 統計數據
## 邊界條件與錯誤場景
### 錯誤場景 1: {錯誤類型}
(測試異常情況)
#### 請求
\`\`\`http
POST /api/v1/{resource}
{
"invalidField": "value"
}
\`\`\`
#### 預期響應
\`\`\`json
{
"error": "ERROR_CODE",
"message": "錯誤訊息說明",
"field": "invalidField"
}
\`\`\`
**HTTP 狀態碼**: `400 Bad Request`
### 錯誤場景 2: {錯誤類型}
...
## 自動化測試範例
### 使用 Jest + MSW
\`\`\`typescript
describe('US-XXX: Scenario N', () => {
it('should {預期行為}', async () => {
// Arrange (Given)
const user = { userId: 'user-001', role: 'ROLE_NAME' };
const payload = { /* ... */ };
// Act (When)
const response = await api.post('/api/v1/resource', payload);
// Assert (Then)
expect(response.status).toBe(201);
expect(response.data.id).toBeDefined();
expect(response.data.tenantId).toBe('tenant-abc');
});
it('should return error when {錯誤條件}', async () => {
const invalidPayload = { /* ... */ };
await expect(
api.post('/api/v1/resource', invalidPayload)
).rejects.toMatchObject({
response: {
status: 400,
data: { error: 'ERROR_CODE' }
}
});
});
});
\`\`\`
### 使用 Cypress (E2E)
\`\`\`typescript
describe('US-XXX: Scenario N', () => {
beforeEach(() => {
cy.login('staff@example.com', 'password');
});
it('should {預期行為}', () => {
// Given
cy.visit('/resource/new');
// When
cy.get('[data-testid="field1"]').type('value1');
cy.get('[data-testid="submit-button"]').click();
// Then
cy.contains('成功訊息').should('be.visible');
cy.url().should('include', '/resource/');
});
});
\`\`\`
## 測試數據維護
### Mock API 配置
(如使用 MSW,說明如何更新 mock handlers)
檔案位置: `src/mocks/handlers/{domain}.ts`
### 種子數據
(如需要特定的測試數據,說明如何準備)
檔案位置: `src/mocks/data/seeds.ts`
## 備註
(其他重要資訊)
目錄結構
sbe/
├── epic-0/
│ └── US-001-user-login/
│ ├── scenario-1-valid-credentials.md
│ ├── scenario-2-invalid-password.md
│ └── scenario-3-locked-account.md
├── epic-2/
│ └── US-201-create-order/
│ ├── scenario-1-individual-customer.md
│ ├── scenario-2-corporate-customer.md
│ └── scenario-3-custom-project.md
└── ...
命名規範
Scenario ID
- 格式:
scenario-{編號}-{kebab-case-描述} - 範例:
scenario-1-individual-customer.md - 編號從 1 開始遞增
場景命名原則
- 使用描述性的名稱
- 強調測試的關鍵條件
- ✅ 好:
scenario-1-individual-customer.md - ❌ 差:
scenario-1.md
撰寫指南
1. Given - 描述前置條件
要點:
- 使用具體的測試數據(真實的 ID、名稱、數值)
- 用 JSON 格式呈現數據結構
- 包含所有與場景相關的初始狀態
✅ 好的範例:
{
"customerId": "cust-123",
"name": "李大華",
"phone": "0912-345-678"
}
❌ 不好的範例:
有一個客戶
2. When - 描述執行操作
要點:
- 提供完整的 HTTP 請求範例
- 包含所有必要的 Headers
- 使用真實的 Payload 數據
✅ 好的範例:
POST /api/v1/orders
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json
{
"customerId": "cust-123",
"items": [...]
}
❌ 不好的範例:
發送創建訂單的請求
3. Then - 描述預期結果
要點:
- 提供完整的響應範例
- 明確列出數據庫變更
- 說明所有副作用(通知、事件等)
✅ 好的範例:
{
"orderId": "ABC-20251031-0001",
"status": "pending_confirmation",
"totalAmount": 2400
}
數據庫變更:
orders表新增 1 筆記錄order_items表新增 2 筆記錄
❌ 不好的範例:
訂單創建成功
4. 邊界條件測試
每個 Scenario 都應包含至少 2-3 個錯誤場景:
- 缺少必填欄位
- 無效的數據格式
- 權限不足
- 業務規則違反
5. 可執行性
SBE 範例應該:
- 可以直接複製到測試代碼中
- 包含完整的 Arrange-Act-Assert 結構
- 提供清晰的斷言條件
與 Mock API 整合
所有 SBE 場景都應該能在本地 Mock API 環境中執行:
1. 確認 Mock Handler 支援
檢查 src/mocks/handlers/ 是否有對應的 API endpoint。
2. 準備測試數據
在 src/mocks/data/seeds.ts 中添加必要的種子數據。
3. 執行測試
npm run dev # 啟動 Mock API
# 在瀏覽器 Console 或 Postman 中測試 API
Three Amigos 協作
SBE 應由三方協作完成:
-
產品經理
- 提供業務場景與預期行為
- 確認範例數據的真實性
-
開發人員
- 定義 API 請求/響應格式
- 說明數據庫變更細節
-
測試人員
- 設計邊界條件測試
- 撰寫自動化測試代碼
審查檢查清單
提交 SBE 文檔前,請確認:
- Given 包含完整的初始狀態(使用 JSON 格式)
- When 提供完整的 HTTP 請求範例
- Then 包含響應範例、數據庫變更、副作用
- 至少包含 2 個錯誤場景
- 提供可執行的自動化測試範例
- 範例數據真實且有意義(非 foo/bar)
- 所有 JSON 格式正確且完整
- 與 Mock API 一致(可在本地驗證)
- Markdown 格式正確
參考資源
- Specification by Example - Gojko Adzic
- Cucumber/Gherkin
- Example Mapping
最後更新: 2025-10-31