API 功能測試的四個層級
1. 狀態
它有回傳 200 嗎。
能抓到服務中斷。但該有一筆記錄卻回傳空結果時,它照樣開心地通過。
2. 結構
預期的欄位都在嗎,型別是不是預期的那些。
能抓到 schema 漂移。但值對不對,它完全不置一詞。
3. 值
總計是不是真的等於各項相加,狀態是不是這個操作應該產生的那一個。
真正的缺陷從這一層開始被抓出來。
第四層是 關聯:建立訂單之後,去查那位客戶時看得到這筆訂單嗎。最有意思的問題多半藏在兩次呼叫之間,而不是單次呼叫裡面,而第四層是唯一會往那裡看的。
為什麼測試套件停在第二層
因為第三層和第四層必須知道「正確」是什麼意思,也就是說得有人來決定。狀態和結構可以從規格機械式地推導出來;值和關聯則需要對產品做出判斷。
這確實比較難,但價值也全在這裡。一套從不表達任何產品規則的測試,只是用一種很費工的方式在確認服務還活著。
大家會跳過的負面案例
每一個端點都要問:什麼東西應該被拒絕,然後斷言它確實被拒絕。超出範圍的值、缺少的必填欄位、屬於別人的識別碼。回傳 200、卻把錯誤物件塞在 body 裡的服務相當常見,沒有意識到這種模式的測試套件會永遠報綠燈。
測試套件底層需要什麼
有四件事決定一套 API 測試能不能撐過第一年:會過期的 session、只在執行期才存在的值、彼此相依的呼叫,以及沒有人清理的資料。TestSprite 以 Auto-Authentication、Dynamic Variables、Dependency Chains 和 Auto-Cleanup 來處理這四件事,說明請見 API 測試文件。
把這些做對,你才能把注意力放在第三層和第四層,而不是花在基礎設施上。
讓覆蓋率走出淺水區
產生出來的測試計畫會從規格或一次探索掃描開始,涵蓋功能、schema、授權、錯誤處理與邊界等類別,因此負面案例預設就存在,而不是得靠某個人在 sprint 結束前想起來。
終端機
npm install -g @testsprite/testsprite-cli
testsprite setup
如果你不想在本機安裝任何東西,TestSprite 儀表板裡也有同樣的設定方式。CLI 其餘的功能都列在 CLI 儲存庫。
挑選哪些端點該做到第四層
關聯斷言最有價值,成本也最高,所以不該到處都寫。有效的篩選方式是問:答案錯了要付出什麼代價。
只要會動到金流、會改變權限,或是會成為別人決策依據的,就做到第四層。訂單存在卻沒有掛在任何客戶底下、權限授予只在單邊成功、報表本身前後一致卻和底層記錄對不起來。這些失敗既昂貴又安靜。
唯讀的報表端點、健康檢查,以及輸出馬上會有人親眼看過的東西,都不需要。在這件事上刻意取捨,才能讓測試套件跑得夠快,快到大家真的願意跑它。
每次變更都跑一次
有兩條路,而怎麼選其實取決於 pipeline 歸誰管。從儀表板串接 GitHub App 完全不需要改動你的儲存庫,因為它是對你的建置流程本來就會發出的部署做出反應。而加上一個 GitHub Actions 步驟,則會把這道檢查放進儲存庫裡,讓它像建置流程的其他部分一樣被審查。
TestSprite 如何帶你走出第二層
產生出來的測試計畫預設就包含負面案例,橫跨功能、schema、授權、錯誤處理與邊界等類別,而這正是多數手寫測試套件耗盡力氣的地方。你用白話就能調整它們,而你的產品知識正是在這個調整過程中進入測試套件的。
值與關聯這兩層才值得你投注心力,也正是需要懂產品的人參與的地方。Auto-Authentication 讓 session 在整趟執行中保持有效,Dynamic Variables 在呼叫之間傳遞值,Dependency Chains 依據每個案例需要什麼、產出什麼來推導執行順序,Auto-Cleanup 則只清掉這趟執行建立的東西,於是你花的時間都用在思考「正確」是什麼,而不是用在基礎設施上。
你得到的是一套會針對回應內容、也針對呼叫之間關聯做斷言的測試,而且每次變更都會執行,而不是一條只證明服務還活著的綠燈 pipeline。
斷言狀態碼沒有用嗎?
不是,它只是基本盤。留著它,然後為每個重要的端點至少加上一個值層級的斷言。
我們要怎麼決定「正確」是什麼意思?
去問產品承諾了什麼,而不是程式碼做了什麼。如果某個端點沒有人答得出來,這份模糊本身就值得攤開來談。
每個端點都要做滿四個層級嗎?
不用。把第三層和第四層留給值錯了會有後果的端點。報表端點和健康檢查不需要。
那自動產生的斷言呢?
對第一層和第二層很有用,因為那兩層是機械式的。第三層和第四層則值得有人審過,因為產品知識就是從那裡進來的。
每個端點要寫幾個負面案例?
一兩個真正重要的,勝過十個把所有可能的爛輸入都列一遍。挑那些真實客戶端會觸發的拒絕情境。