測試是會被反覆閱讀的規格文件。這一章整理一套測試文字的紀律——每一條都來自實際 review 中被糾正的寫法,附改寫前後的對照。核心原則一句話:測試名稱與斷言負責「說內容」,註解只負責「說操作約束」,其他文字都是雜訊

一、測試內容由斷言自述,不寫敘述性註解

測試名稱就是主張、斷言就是內容——讀了名稱和斷言還無法判斷測試驗證什麼,該修的是名稱和斷言,不是加「本測試對應/驗證什麼」的說明註解。

名稱還承擔一個失敗訊息給不了的東西:失敗輸出只有症狀(Expected X、Actual Y),拿到紅燈的人要逆推「為什麼這個行為是刻意的」,而回答它的位置就是名稱。「直接進入下一階段:不做編輯流程的收尾」讓紅燈的人知道自己撞到的是一條刻意的規則、不是漏網的 bug;名稱說不出這件事為什麼成立,先改名稱,改完仍說不出來才考慮註解。

允許留下的註解只有兩種:

類型例子為什麼留
操作性約束「本檔不可初始化 UI 測試綁定——它會把 HTTP client 換成假件、擋掉真實網路」違反會壞、且原因不可能從程式碼看出
非顯而易見的 setup 原因「先復原現場再下判定,斷言失敗也不留佔用的資源」「延遲重讀,容許後端非同步生效」不解釋的話,這些步驟看起來像多餘或錯序

二、reason 寫失敗的後果與處置,不寫感想

斷言的失敗訊息是「未來某個紅燈時刻」的第一線資訊,它的讀者正急著知道兩件事:這代表什麼、接下來做什麼。

  • 改寫前:reason: '狀態應該是 1'
  • 改寫後:reason: '後端未釋放資源(即刻=X、延遲=Y)——前端刪除編排與假後端都依「同步釋放」設計,需與後端確認並同步修正兩處。'

把變數值內插進訊息(實際讀到的狀態、id),紅燈時不用重跑加 log。

三、檔頭陳述目的,不論證需求

檔頭回答三件事就夠:這是什麼、怎麼用、維護時要做什麼。存在理由的論證(「因為 stub 只會回放假設,所以需要這個假後端,否則⋯⋯」)屬於教材或 PR 說明,不屬於程式碼——讀程式的人需要的是操作資訊,不是被說服。

同理,彙整清單不放檔頭。「本假後端已模擬的行為:合併=⋯、更新=⋯、刪除=⋯」這種清單,每一條在對應的 handler 方法上都有自己的說明——檔頭清單是重複,而且沒有任何機制守著它與 handler 同步,終將過期。

四、取證出處、日期、開發過程不入程式碼

取證出處、日期與開發過程回答的是「怎麼知道的」;測試文字只需要回答「行為是什麼」。

  • 改寫前:「合併後保留記錄 id(某年某月實測證實)」「本測試曾以相反順序抓到此問題」
  • 改寫後:「合併後記錄 id 不變(後端只改外鍵)」/刪除

取證紀錄和開發史寫進版本控制的 commit 訊息、團隊的知識庫即可。程式碼裡的註解只陳述當前為真的行為——帶日期的註解從寫下那刻就開始腐化,「曾經抓到」的敘述在下一個讀者眼中只是噪音。

五、分析詞彙不入測試內容

團隊在討論問題時會發展出後設詞彙——「語意」「契約」「漂移」這類幫助人類對齊理解的抽象詞。它們屬於對話,不屬於測試:

  • 改寫前:測試名「後端語意:刪除單據釋放資源」、reason「語意漂移:⋯」
  • 改寫後:測試名「刪除單據一併釋放關聯資源」、reason「後端未釋放資源——與前端編排依據的行為不符⋯」

判斷法:這個詞刪掉之後句子有沒有變得更直接?「刪除單據一併釋放關聯資源」是可以直接對後端行為驗證的陳述句;「後端語意:⋯」是套在陳述句外面的分類標籤,資訊量為零。

六、用動作+結果的白話取代自創行話

白話的標準是「動作+結果」:句子直接說出做了什麼、預期看到什麼,讀者不需要先學會團隊內部的簡稱。

  • 改寫前:「驗證早退情境不打後端」
  • 改寫後:「讓測試能斷言『提前結束的路徑(狀態未推進、空清單)沒有發出請求』」

判斷法:這個詞在程式碼或團隊詞彙表裡存在嗎?讀者第一次看到需要停下來猜嗎?「早退」「打後端」「塞 mock」這類壓縮語,寫的人省了五個字,每個讀者各付一次理解成本。

七、跳過訊息要可行動

測試被跳過時,輸出裡那行 skip 訊息是讀者唯一的線索。

  • 改寫前:skip: '未提供憑證'
  • 改寫後:skip: '未提供憑證,跳過真實後端驗證——帶 <具體參數> 執行(見檔頭)'

原則與 reason 相同:告訴讀者這代表什麼、怎麼讓它跑起來。

彙總判斷表

想寫的內容該放哪
這條測試驗證什麼測試名稱
失敗代表什麼、怎麼處置expect 的 reason
違反會壞的環境約束檔頭註解
不解釋會看不懂的 setup 步驟該行上方一行註解
存在理由的論證、設計取捨教材/PR 說明
取證過程、日期、開發史commit 訊息/知識庫
分析用的後設詞彙對話裡,用完就留在對話
怕有人改壞某個約束一條會紅的測試

最後一列跟其他列的性質不同。前面幾列都在分配「這段文字該放哪個 surface」,而它是在說這段內容根本不是文字問題——寫下它的動機是防護,而註解不參與執行、改壞的當下不會發聲。本篇第一節允許留在檔頭的那類註解(「本檔不可初始化 UI 測試綁定」)就是這個形態的邊界案例:它守的是環境約束,而環境約束沒有任何測試接得住,所以留在檔頭是對的。判定方式與當場可執行的驗證見 #253 寫註解的動機是怕被改壞時要處理的是那個約束

下一步路由