跨國家擴充測試資料的難點,通常不在於把清單從幾個加長到幾十個,而在於每加一個就要多面對一組新的差異。如果每一組差異都得手寫一段測試,維護成本會以令人無法接受的速度成長。以下說明怎麼把這件事變成可累積的工作。
從幾個地方擴到幾十個地方,難在哪裡?
難在差異的種類會增加,而不是數量會增加。十個地方可能剛好都落在同一種規則下,於是看起來一切正常;到了三十個地方,突然出現幾種完全不同的結構,而原本的斷言假設全部失效。
第二個難點是組合爆炸。當每個地方都有若干可選欄位時,兩兩組合的數量成長得比地方數量快得多,於是不可能逐一窮舉。第三個難點是失敗的來源變得模糊:同一個紅燈可能來自資料本身、來自斷言假設,也可能來自某個地方的例外。
| 擴充階段 | 主要困難 | 應該優先做的事 |
|---|---|---|
| 少量地方 | 差異還看不出來 | 把共同規則寫成不變量 |
| 中量地方 | 開始出現例外 | 建立例外清單與負責人 |
| 大量地方 | 組合無法窮舉 | 轉向抽樣與風險排序 |
| 持續新增 | 前人例外無人理解 | 讓每條例外都附理由 |
理解這四個階段,可以避免在中量階段就急著引入複雜的產生器,也避免在大量階段還試圖窮舉。
階段的判斷也可以反過來用:如果團隊正在為某一類例外反覆撰寫特例,那通常代表不變量的切分方式需要重新檢視,而不是例外還不夠多。
把斷言分成不變量與例外清單
這是最有效的一個切分。不變量是所有地方都必須成立的事,例外清單則記錄那些不遵守某些不變量的地方與原因。兩者分開之後,新增一個地方就只是「通過全部不變量,並登記它的例外」。
不變量的例子包括:每個項目都能被選中、選中之後都能被還原、顯示名與儲存鍵不會互相汙染、匯出之後能再匯入。這些條件與地方的具體結構無關,因此可以穩定地套用到所有地方。
例外清單則要寫得足夠具體,讓後人能判斷它是否還成立:
- 它不遵守哪一條不變量。
- 為什麼不遵守,依據是什麼。
- 從什麼時候開始,預計什麼時候重新檢視。
- 誰負責判斷它是否仍然適用。
這份清單的價值會隨著時間增加。它同時是測試的輸入,也是新人理解系統的第一份讀物。
清單還有一個容易被忽略的用途:它是訓練材料。比起直接閱讀程式碼,先讀一份寫明「哪些地方不遵守哪些規則」的清單,能更快建立起對系統差異的整體認識。
資料產生要能重現
不能重現的測試資料等於沒有測試資料。當某個案例只在某一次執行中失敗,而沒有人能再讓它失敗一次,這個案例最終只會被標記為不穩定並被忽略——而它可能正好抓到了真問題。
因此資料產生要由固定的輸入決定,而不是由當下的時間或執行順序決定。實作上建議注意以下幾點:
- 以固定的起始條件產生,讓同一組種子永遠得到同一批資料。
- 產生的樣本要能對應到具名的案例,而不是只看得到一堆隨機值。
- 記錄每一批資料的產生條件,失敗時可以直接重跑同一批。
- 把產生器本身也納入版本管理,讓資料與程式同步演進。
要能重現還有一個附帶條件:樣本的存在理由要被寫下來。否則後人只會看到一堆長得差不多的資料,既不敢刪也不知道能不能改。數值相關的檢查該如何分層,可以對照號碼驗證的運作方式那篇。
什麼時候該抽樣而不是全量?
當全量的邊際價值低於它的執行成本時。抽樣不是偷懶,而是一種資源配置的決定,前提是你知道自己在抽什麼。
抽樣的正確做法是依風險分層,而不是隨機取幾個:
| 抽樣方式 | 適用情境 | 風險 |
|---|---|---|
| 依結構分層 | 差異來自欄位結構 | 分層定義錯就會漏掉一整類 |
| 依歷史故障分層 | 曾出錯的地方優先 | 新地方可能完全沒被覆蓋 |
| 依使用頻率分層 | 常見情境優先 | 罕見但嚴重的問題被延後發現 |
| 全量 | 改動涉及共用邏輯 | 執行時間長,但最能給出保證 |
判斷的關鍵是「這次改動碰到的是共用邏輯還是單點邏輯」。碰到共用邏輯時,全量的價值很高,因為一個錯誤會同時影響所有地方;只碰單點時,抽樣加上針對該點的專項案例通常就足夠。
分層還有一個實務要求:分層的定義要能被檢查。如果某個地方從未被任何一層涵蓋,那它其實是在抽樣之外,而不是被抽到。建議定期執行一次「哪些地方最近沒有被任何測試碰到」的查詢,把它當成健康指標。
新增一個地方要跑哪些檢查?
新增是擴充流程中最關鍵的一步,因為它同時引入資料與假設。把它固化成一份清單,可以避免每次都由不同的人用不同的標準處理。
- 它的鍵在現有資料裡是否唯一,是否與既有鍵衝突。
- 它需要哪些欄位,哪些欄位對它不適用。
- 它是否違反任何一條不變量,若違反則登記例外。
- 它是否需要一組專屬的樣本,樣本的理由是否已寫下。
- 它的顯示名在各種語言下是否都已具備。
- 它是否會影響依賴既有清單的彙總與分組。
這份清單的另一個用途是回顧:當某個地方被移除時,可以對照同一份清單逐項撤銷,而不會留下孤兒資料。
給開發者:把例外清單放進版本控制
例外清單不該只存在於某個人的筆記或對話裡。它必須與程式一起被版本管理,才能在改動時被看見、被討論、被要求附上理由。
- 例外清單與不變量放在同一個版本庫,一起被審查。
- 每一條例外都註明依據與檢視時間,讓過期能被發現。
- 讓「不變量全部通過」成為合併前的必要條件,例外則需明確核准。
- 定期移除已不成立的例外,避免清單成為沒人敢動的紀念碑。
- 讓新增地方與新增例外走同一條流程,避免有人的路徑比較快。
本文的不變量敘述、例外條目與案例結構都是示意性質的合成內容,用來示範清單可以長成什麼樣子,並非真實系統的測試報告、缺陷紀錄或需求文件。要把這些原則套回具體的選擇流程,可以讀國家選擇欄位測試;而資料本身的保鮮問題,見國家資料新鮮度。
下一步
把你目前所有的地方隨便挑一個,寫出它不遵守的那條規則並附上理由。若寫不出來,那條規則其實是通用的,應該被提升為不變量。要列出完整的地方清單與欄位樣貌,可以從國家與地區目錄開始。