後端面試:如何用消費者驅動契約測試安全發布微服務 API?
題幹與適用場景
一個訂單 Provider 被 Web、行動端與非同步訊息消費者使用。團隊希望在 Provider 發布前發現破壞性變更,但不想每次提交都啟動所有真實服務。請用消費者驅動契約測試設計流程,說明消費者契約寫什麼、Provider 如何驗證、共享 Broker 如何選版本、如何處理 Provider state,以及何時允許部署。
這題考察 API 邊界、測試分層、版本相容與持續交付。Pact 將契約建模為消費者需要的請求和最小回應,並在 Provider 端重播驗證;它不取代單元、整合或端到端測試。
面試官在考察什麼
- 能否把消費者行為轉成最小互動契約,而不是複製 Provider 全部實作。
- 能否解釋 mock consumer test、provider verification、Broker 和部署矩陣的完整資料流。
- 能否用 Provider state 隔離前置資料,避免測試依賴共享資料庫或隨機真實資料。
- 能否處理多版本消費者、未驗證契約、訊息契約和失敗回滾。
先釐清哪些問題
- 是 HTTP、非同步訊息,還是兩者都有?訊息契約和 HTTP 互動的載體不同。
- 哪些欄位是真正被消費者使用的?契約應只表達必要請求和最小回應。
- Provider state 能否在測試環境建立、清理和參數化?依賴是否需要 stub?
- 版本如何標識,生產部署前需要滿足哪些消費者/Provider 組合?
- 是否有 Broker、分支標籤、pending pact 和回滾策略?
30 秒回答框架
消費者先用 mock 寫互動測試,產生只含必要請求和回應斷言的 Pact;契約發布到 Broker。Provider 驗證器取回目標消費者版本,準備 Provider state,在本機或隔離環境重播請求並發布驗證結果。CI 的部署閘門查詢目前版本組合是否已驗證,未驗證的新契約先進入 pending 或阻止發布。契約只保證訊息形狀和互動,不承擔全部業務正確性;失敗時停止部署、修復 Provider 或回滾到仍被驗證的版本。
分步作答
1. 從消費者行為定義互動
每個互動描述請求方法、路徑、必要標頭、關鍵請求欄位和最小回應斷言。消費者測試呼叫 Pact mock,而非 Provider 真實實例;這樣能快速產生可分享契約檔案,並讓斷言跟隨消費者實際使用。
consumer test -> pact file -> broker
provider verifier + provider state -> replay -> verification result
deployment gate -> compatible version matrix -> deploy or block2. 讓契約保持最小且穩定
不要斷言未使用的回應欄位、隨機 ID 或完整資料庫快照。使用 matching 表達型別、格式和必要結構;時間、UUID 等動態值只驗證形狀。契約測試關注通訊訊息,不應變成 Provider 的通用功能測試。
3. 設計 Provider state
Provider state 是每個互動的前置條件,例如「訂單已付款」。驗證前透過 state handler 建立或 stub 資料,驗證後清理。狀態參數必須可追蹤、冪等且不洩露生產憑證;不要讓多個互動共享不可控順序。
4. 用 Broker 管理版本和驗證
消費者發布契約並帶上分支、版本或環境標籤;Provider 驗證目標消費者版本並回寫結果。部署閘門檢查候選 Provider 版本與即將部署的消費者版本組合,而不是只看「最新契約是否綠」。對新出現但尚未驗證的契約,可使用 pending 行為先驗證並記錄,再決定是否阻斷。
5. 選擇發布順序與相容窗口
相容變更通常先部署能相容舊請求和舊回應的新 Provider,再升級消費者;破壞性變更需要雙讀/雙寫、版本化路徑或遷移窗口。非同步訊息使用 message pact 驗證訊息端口,仍需測試真實 broker 的投遞、重試和順序語意。
6. 失敗處置與觀測
記錄契約失敗的互動、Provider state、版本、提交 SHA 和環境。失敗時阻止部署並回滾到最後一個通過部署矩陣的版本;上線後繼續觀察 4xx/5xx、反序列化錯誤、訊息死信和業務指標。測試綠不代表生產一定正確,必須保留執行期保護。
高品質示範答案
我會讓每個消費者用 Pact mock 編寫真實互動,產生只含必要請求和最小回應的契約並發布到 Broker。Provider 驗證器按消費者版本選擇契約,在隔離環境執行可重複的 Provider state,重播請求並把結果、Provider 版本和提交 SHA 回寫。CI 部署閘門查詢即將組合的消費者/Provider 版本是否都有驗證記錄;pending pact 可讓新契約先被驗證而不立即破壞舊 Provider。
契約只覆蓋通訊形狀和使用欄位,不能取代單元、整合、端到端及業務測試。相容演進先保證 Provider 讀舊請求、回傳舊消費者能理解的回應,再升級消費者;破壞性變化使用版本化路徑或雙寫窗口。失敗時阻斷發布並回到最後通過矩陣的版本,同時保留執行期錯誤、死信和業務指標觀測。非同步場景用 message pact 驗證訊息內容,但另行驗證 broker 語意。
常見失分點
- 把契約寫成 Provider 全量回應快照,導致無關欄位變化頻繁阻斷。
- 只執行消費者 mock,不在 Provider 實例上重播驗證。
- Provider state 依賴共享資料庫順序、隨機資料或生產憑證。
- 只比較最新版本,不檢查實際部署組合和長尾消費者。
- 把 Pact 當成端到端測試,忽略真實 broker、權限、效能和業務規則。
- 驗證失敗仍繼續部署,或沒有可驗證的回滾版本。
追問與參考回答
為什麼契約回應要最小化?
消費者只應鎖定真正依賴的欄位。最小回應減少偶然耦合,讓 Provider 可以新增不影響消費者的欄位。
Provider state 和測試 fixture 有什麼差別?
Provider state 是互動前置條件的可執行介面,可由驗證器在 Provider 環境設定和清理;fixture 只是測試資料載體,未必能跨服務建立正確狀態。
pending pact 解決什麼問題?
它允許新契約先進入 Broker 並執行驗證,在首次出現且尚未被 Provider 證明時不立即讓舊 Provider 建置失敗;是否啟用要結合組織發布策略。
如何避免契約數量失控?
按真實消費者互動去重,刪除已退役版本,限制隨機欄位和全量快照,並讓 Broker 記錄 owner、版本和最後驗證時間。
消費者版本選擇為什麼重要?
部署閘門要驗證實際即將協同運行的版本組合;只驗證 main 或 latest 可能漏掉生產中的舊行動端或分支版本。
Pact 能證明 Provider 的業務正確嗎?
不能。它證明互動滿足消費者契約;業務不變量、效能、授權、故障恢復和真實基礎設施仍需其他測試與生產觀測。