契約測試檢查什麼

檢查雙方預期的結構是不是同一個結構。欄位名稱、型別、必填與選填、狀態碼。如果提供者移除了某個消費者依賴的欄位,契約測試會在建置階段就失敗,而不是在凌晨三點。

對於服務眾多、各自獨立部署的系統來說,這確實很有價值,而且比端對端整合環境便宜得多。

它刻意不檢查什麼

  • 值是不是對的。 一個結構正確、但總計金額算錯的回應,依然符合所有契約。

  • 流程走不走得通。 每一次呼叫都可以符合自己的契約,整段順序卻產生毫無意義的結果。

  • 使用者看到什麼。 契約談的是服務與服務之間的對話。沒有人真的打開過產品。

這些都不是批評。會檢查值的契約測試會和資料耦合在一起,然後不斷壞掉,而這正是契約測試當初要避開的事。範圍窄本身就是它的優點。

要留意的失敗模式

團隊導入契約測試,整合失敗不再發生,信心跟著上升。六個月後,正式環境壞在一個所有契約測試都通過的地方,因為結構是對的,行為是錯的。人們通常得出的結論是契約測試失敗了,而這是錯誤的結論。

契約測試做好了它該做的事。缺口在於沒有任何東西在檢查行為,而契約這一層的成功,反而讓這個缺口更難被看見。

該和它搭配的是什麼

針對執行中的服務做功能驗證:這一連串呼叫是否產生正確的結果、錯誤路徑是否如預期運作、授權是否守得住。那是另一個層次,回答的是契約無法回答的問題。

這些檢查需要處理工作階段、在呼叫之間擷取的值、執行順序與清理,詳見 API 測試文件。

終端機

npm install -g @testsprite/testsprite-cli
testsprite setup

如果你不想安裝任何東西,儀表板也能做到同樣的事。命令列能做的其他所有事情,都收錄在 CLI 儲存庫。

消費者驅動,以及它為什麼通常勝出

這兩種做法的差別在於契約由誰來寫,而這個差別不會立刻顯現,要一年後才看得出來。

提供者驅動的契約記錄的是服務打算提供什麼。這有它的用處,但通常會開得太寬:契約描述的是完整的回應,包含沒有人使用的欄位。於是連沒人用的欄位一有破壞性變更都會讓建置失敗,團隊開始忽略契約的失敗,這就失去了意義。

消費者驅動的契約記錄的是每個消費者實際讀取了什麼。契約更小,失敗都是真的,而且你會得到一個真正有用的副產物:一張精確的對照表,指出哪個欄位對誰重要。正是這張表,讓提供者能夠有把握地淘汰某個東西,而這其實才是大家真正想要的。

各自該在哪裡執行

契約測試放在建置階段,跑得快,而且每次提交都跑,因為它全部的價值就在於整合之前先攔下結構偏移。行為驗證則針對已部署的環境執行,每一個 pull request 都跑一次。

觸發的時機比機制更重要。把它掛在部署事件上,就代表每一次變更都會被檢查,不需要任何人特別決定; GitHub App 從儀表板就能做到這件事,而 GitHub Actions 步驟則是在你的工作流程裡完成同一件事。

TestSprite 能回答而契約無法回答的

值是不是對的,以及整段順序有沒有產生正確的結果。它透過介面實際操作執行中的服務,所以一個符合所有契約、卻帶著錯誤總計的回應會被抓出來。

多步驟流程正是那個缺口。Integration Tests 會把呼叫串接起來,讓前一個回應餵給下一個;Dependency Chains 則根據每個案例需要什麼、產出什麼,推導出安全的執行順序。Auto-Authentication 讓工作階段在整趟執行中保持有效,Dynamic Variables 在呼叫之間傳遞值,Dependency Chains 依據每個案例的需求與產出推導執行順序,Auto-Cleanup 則精準移除這趟執行所建立的東西。

最後你得到的是兩個互不重疊的層次:契約在建置階段以低成本、每次提交都執行的方式攔下結構偏移;行為驗證則接手契約刻意視而不見的正確性問題。

TestSprite 做契約測試嗎?

不做。它針對執行中的服務驗證行為。契約測試是另一種工具,兩者互為補充。

對微服務來說,契約測試夠嗎?

對於服務之間的結構偏移,它是正確的答案;但它不會告訴你跨多個服務的流程是否產生正確的結果。

消費者驅動還是提供者驅動?

消費者驅動能抓到更多真正的問題,因為它記錄的是消費者實際使用了什麼,而不是提供者打算提供什麼。

我們還需要整合測試嗎?

需要的數量會變少,這正是重點。保留一小組涵蓋跨服務流程的測試,結構的問題就交給契約處理。

如果兩邊都由我們自己掌控呢?

價值會下降,因為你可以把兩邊一起改。它最有用的場景,是各團隊獨立部署的時候。

簡短版

結構正確,行為未知。

契約測試能以低成本攔下服務之間的結構偏移,並且刻意對任何一方是否正確視而不見。把它留在建置階段,並在旁邊搭配針對執行中服務的行為驗證。