測試在持續交付中的角色#
測試是持續交付的品質保障環節。沒有完善的測試體系,快速交付就是「快速製造問題」。
持續交付不是要跳過測試,而是要透過自動化讓測試更快、更可靠、更全面。
測試金字塔#
測試金字塔模型建議測試的分布:
flowchart TB
subgraph Pyramid["測試金字塔"]
E2E["端對端測試<br/>(少量)"]
INT["整合測試<br/>(適量)"]
UNIT["單元測試<br/>(大量)"]
end
E2E --> INT --> UNIT
style E2E fill:#ffcdd2
style INT fill:#fff3e0
style UNIT fill:#c8e6c9| 層級 | 數量 | 速度 | 維護成本 | 信心度 |
|---|---|---|---|---|
| 單元測試 | 多 | 快 | 低 | 中 |
| 整合測試 | 中 | 中 | 中 | 中高 |
| 端對端測試 | 少 | 慢 | 高 | 高 |
程式碼靜態檢查#
靜態程式碼分析是在不執行程式的情況下,透過分析原始碼找出潛在問題。它是測試的第一道防線。
靜態檢查的價值#
統計資料顯示,70% 左右的程式碼邏輯設計和編碼缺陷屬於重複性錯誤,完全可以透過靜態程式碼分析發現和修復。
可以發現的問題類型:
- 空指針風險
- 資源洩漏
- 未使用的變數
- 錯誤的 API 使用
- 不良的編碼風格
- 安全漏洞
SonarQube 實踐#
SonarQube 是業界廣泛使用的程式碼品質管理平台。
核心功能:
| 功能 | 說明 |
|---|---|
| 多語言支援 | Java、JavaScript、Python、Go 等 20+ 語言 |
| 規則管理 | 可自訂檢查規則集 |
| 品質門禁 | 設定品質閾值,未達標則失敗 |
| 技術債務 | 追蹤和量化技術債務 |
| 整合能力 | IDE 插件、CI/CD 整合 |
六步實施流程:
flowchart TD
A["1. 搭建 Sonar 服務"] --> B["2. 組態檢查規則 (Profile)"]
B --> C["3. IDE 安裝 SonarLint 插件"]
C --> D["4. CI/CD 整合掃描"]
D --> E["5. 程式碼審查整合結果"]
E --> F["6. 發布前品質門禁"]
style F fill:#c8e6c9Maven 整合:
mvn sonar:sonar \
-Dsonar.host.url=http://sonar.internal \
-Dsonar.projectKey=my-project品質門禁設定範例:
quality_gates:
- metric: coverage
operator: LESS_THAN
error: 80
- metric: duplicated_lines_density
operator: GREATER_THAN
error: 3
- metric: blocker_violations
operator: GREATER_THAN
error: 0跳過檢查的處理#
有些情況下需要排除特定程式碼的檢查:
1. 排除整個文件
# sonar-project.properties
sonar.exclusions=**/generated/**,**/vendor/**2. 排除特定規則
@SuppressWarnings("squid:S1135") // 忽略 TODO 警告
public void someMethod() {
// TODO: implement this
}3. 調整規則等級
將某些規則從「錯誤」降級為「警告」,或完全停用。
跳過規則應該謹慎使用,並記錄原因。過度使用會讓靜態檢查失去意義。
破壞性測試#
破壞性測試(也稱為混沌測試)是通過故意製造故障來驗證系統的健壯性。
為什麼需要破壞性測試#
傳統測試關注「正常情況下系統是否工作」,而破壞性測試關注「異常情況下系統是否能夠正確處理」。
分散式系統的常見弱點:
- 服務不可用時的回退能力不足
- 超時設定不合理導致的重試風暴
- 依賴服務流量過大導致的級聯故障
- 單點故障引起的整體癱瘓
混沌工程#
混沌工程是在生產系統上進行受控實驗,以建立對系統健壯性的信心。
混沌工程的四個步驟:
定義「穩定態」
- 確定系統正常運行的可觀測指標
- 例如:請求成功率 > 99.9%,延遲 P99 < 500ms
建立假設
- 假設在引入故障後,系統仍能維持穩定態
- 例如:即使一台伺服器宕機,整體服務仍可用
引入真實故障
- 伺服器崩潰
- 網路中斷/延遲
- 資源耗盡(CPU、記憶體、磁碟)
驗證假設
- 比較實驗組和對照組
- 發現系統弱點
混沌工程原則:
| 原則 | 說明 |
|---|---|
| 使用真實事件 | 模擬真實可能發生的故障 |
| 生產環境驗證 | 在真實環境才能發現真實問題 |
| 自動化持續 | 將實驗自動化,持續執行 |
| 最小爆炸半徑 | 控制影響範圍,降低風險 |
Netflix Chaos Monkey
Netflix 是混沌工程的先驅,他們開發了 Chaos Monkey 系統。
Chaos Monkey 的工作方式:
- 在工作日隨機終止生產環境的服務實例
- 強迫工程師設計能夠容忍故障的系統
- 持續驗證系統的復原能力
Netflix 的名言:
「避免失敗的最好辦法就是經常失敗。」
Chaos Monkey 家族:
- Chaos Monkey:隨機終止實例
- Latency Monkey:注入延遲
- Chaos Kong:模擬整個區域故障
- Chaos Gorilla:模擬可用區故障
實施建議:
- 從非生產環境開始
- 先進行小規模實驗
- 建立完善的監控和告警
- 準備好回滾方案
- 逐步擴大實驗範圍
破壞性測試的執行策略#
破壞性測試具有真實的破壞力,必須謹慎規劃和執行。
測試時機:
- 單元測試和功能測試之後
- 在隔離的測試環境中
- 逐步推進到類生產環境
兩個維度的測試設計:
維度一:特定故障點測試
- 設計特定的故障場景
- 驗證系統的錯誤處理邏輯
- 確認系統能否按預期恢復
維度二:探索性故障測試
- 壓力測試找出瓶頸
- 隨機故障注入
- 鏈路阻斷測試
Mock 與回放技術#
自動化回歸測試面臨三大挑戰:
- 測試資料的準備和清理
- 分散式系統的依賴
- 測試用例的仿真度
Mock 和回放技術可以幫助解決這些問題。
Mock 技術#
Mock 是透過模擬來替代真實依賴的技術。
Mock 帶來的價值:
| 價值 | 說明 |
|---|---|
| 獨立性 | 測試不依賴外部服務 |
| 速度 | Mock 返回比真實呼叫快 |
| 可控性 | 可以模擬各種邊界情況 |
| 穩定性 | 不受外部服務不穩定的影響 |
基於對象的 Mock(單元測試):
常用框架:Mockito、EasyMock
// Mockito 範例
@Test
public void testOrderService() {
// 創建 Mock 對象
PaymentService paymentService = mock(PaymentService.class);
// 定義 Mock 行為
when(paymentService.pay(any(Order.class)))
.thenReturn(new PaymentResult(true, "SUCCESS"));
// 執行測試
OrderService orderService = new OrderService(paymentService);
OrderResult result = orderService.createOrder(order);
// 驗證
assertTrue(result.isSuccess());
verify(paymentService, times(1)).pay(any());
}基於服務的 Mock(整合測試):
常用框架:WireMock、MockServer
// WireMock 範例
stubFor(post(urlEqualTo("/api/payment"))
.withRequestBody(matchingJsonPath("$.orderId"))
.willReturn(aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withBody("{\"success\": true}")));
// 測試程式碼可以正常呼叫 /api/payment回放技術#
回放技術是記錄真實的用戶請求,然後在測試環境中重放。
記錄用戶請求的方法:
方案一:SLB 層攔截
flowchart LR
U["用戶"] --> SLB["SLB"]
SLB --> S["伺服器"]
SLB -.->|複製| R["記錄器"]
style R fill:#e3f2fd方案二:伺服器端攔截
flowchart LR
U["用戶"] --> S["伺服器"]
S -.->|軟交換複製| R["記錄器"]
style R fill:#e3f2fd回放的多樣性:
| 模式 | 說明 | 用途 |
|---|---|---|
| 原樣回放 | 按記錄的時間間隔回放 | 功能測試 |
| 加速回放 | 壓縮時間間隔 | 壓力測試 |
| 篩選回放 | 只回放特定類型請求 | 邊界測試 |
| 循環回放 | 重複回放 | 持續測試 |
回放測試不需要記錄所有請求,抽樣記錄即可。這樣可以保持用例的新鮮度,又能控制存儲成本。
整合 Mock 和回放#
攜程的 Mock Service 實踐:
flowchart TB
subgraph MS["Mock Service"]
M1["記錄所有服務的請求/回應"]
M2["如果有匹配的記錄,直接返回"]
M3["如果沒有匹配,透傳到真實服務"]
end
A["服務 A"] --> MS
B["服務 B"] --> MS
C["服務 C"] --> MS
style MS fill:#e3f2fd優勢:
- 無需編碼,組態即可使用
- 基於真實請求,仿真度高
- 統一管理,便於維護
測試策略整合#
持續交付中的測試階段#
flowchart TD
A[程式碼提交] --> B[靜態檢查<br/>SonarQube]
B --> C[單元測試 + Mock]
C --> D[整合測試<br/>Mock Service]
D --> E[端對端測試<br/>真實環境]
E --> F[回放測試 + 破壞性測試]
B -.- B1((第一道防線))
C -.- C1((快速反饋))
D -.- D1((服務邊界驗證))
E -.- E1((最終驗證))
F -.- F1((持續驗證))
style B fill:#e3f2fd
style C fill:#e8f5e9
style D fill:#fff3e0
style E fill:#fce4ec
style F fill:#f3e5f5測試效率最佳化#
| 策略 | 說明 |
|---|---|
| 並行執行 | 同時運行多個測試 |
| 分層快取 | 跳過未變化部分的測試 |
| 增量測試 | 只測試受影響的程式碼 |
| 智能選擇 | 根據變更影響選擇測試用例 |
總結#
測試是持續交付的品質保障基石,需要多種測試方法的組合。
關鍵要點:
靜態檢查
- 是測試的第一道防線
- 可以發現 70% 的重複性錯誤
- SonarQube 是主流選擇
破壞性測試
- 驗證系統的健壯性
- 混沌工程是高級實踐
- 需要謹慎規劃和執行
Mock 和回放
- Mock 解決依賴和隔離問題
- 回放提供高仿真測試用例
- 可以組合使用
測試策略
- 遵循測試金字塔
- 適當組合不同測試方法
- 持續最佳化測試效率
測試的目標不是追求 100% 覆蓋,而是用合理的成本發現盡可能多的問題。選擇正確的測試策略,比追求數字指標更重要。