Community程式設計與開發github.com

ShortArrow/test-design

從規格推導測試案例,避免依賴實作、漏掉邊界、或只測試快樂路徑。適用於撰寫測試計畫、審查套件完整性,以及當測試皆為綠燈但仍缺乏驗證力時。

test-design 是什麼?

此技能專注於從規格(而非實作)推導測試案例,以確保測試套件能真正捕捉缺陷。它明確定義了多個關鍵時刻,例如:當你正要重複快樂路徑、從實作推導測試、忽略未命名邊界、或遺漏錯誤路徑時,應採用的替代方法。技能強調等價類別劃分(每個類別一個測試即可)、邊界值分析(測試邊界內外鄰近值)、決策表(同時涵蓋兩個條件失敗的組合)、以及狀態表(確保每個轉換都有測試案例)。支援平台包括 Claude Code、Cursor 等 AI 代理開發工具,適用於撰寫測試計畫、使用個案規格、為既有程式碼添加測試、審查套件缺口,以及當套件全綠但證明力不足時。其工作方式是將規格視為測試案例的來源,並透過計數(類別、邊界、規則、轉換)而非主觀判斷來評估套件完整性。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ShortArrow/skills/tree/HEAD/skills/test-design

Installed? Explore more 程式設計與開發 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

測試設計

案例決定套件能捕捉什麼。 之後的一切——測試框架、執行器、計數——只是執行那個決定。

這個技能取代的時刻

正要…改為…
只寫快樂路徑就停止遍歷輸入空間:針對每個參數,列出其類別與邊界
從實作推導案例從規格推導,讓實作給你驚喜
用一個舒適的值測試測試每個邊界內的最後一個值與邊界外的第一個值
只測試規格提到的邊界每個範圍都有兩端;句子遺漏的那端仍然是邊界
枚舉規格列出的角色加上呼叫者遺漏的:無角色、未認證、未指派
從快樂路徑變化一個條件建立決策表,涵蓋兩個條件同時失敗的規則
畫出狀態表就繼續在每一行放測試 ID;「不允許」這行最需要測試
將「拒絕,或接受但前提是‥‥」寫成預期結果每個案例只有一個結果;無法決定的規格是開放問題,不是測試
跳過不太可能的錯誤路徑每個失敗模式都是一個類別:無效輸入、資源缺失、拒絕、逾時、半完成
留下未實作的路徑,沒有測試、跳過標記或無人斷言的樁寫一個測試,確認該路徑目前被拒絕為未實作;它現在通過,當有人實作那天變紅(見下方)
只測試功能的主要流程從使用案例讀出替代與例外流程;每個都是一個類別
透過閱讀來評定套件是否充分對照規格計數:類別、兩端邊界、規則、轉換
在已涵蓋的類別中增加第五個測試把這測試花在未涵蓋的類別上——第五個測試買到的是執行時間,而非涵蓋率

攻擊規格,而非實作

透過閱讀程式碼寫出的測試,斷言的是程式碼的行為。 它一出現就通過,將當前的錯誤編碼為期望, 並且在變更後失敗時只說「程式碼變了」, 這本來就是已知的。 這不是猜測,而是可測量的:在 24 個 Java 儲存庫中, LLM 生成的 oracle 傾向於捕捉程式的實際行為,而非預期行為 (Konstantinou, Degiovanni, Papadakis, arXiv:2410.21136)。

從東西應該做的事推導——規格、 線路格式、合約——然後才執行。 唯一的正當例外是特意鎖定當前行為的特性描述測試, 並且必須標記清楚,以免日後有人將鎖定的錯誤誤認為需求。

一個有用的氣味:如果每個測試在第一次執行時都是綠燈, 那麼無論意圖為何,該套件都是從實作推導出來的。

等價類別:計算類別,而非測試

程式碼無法區分的輸入形成一個類別, 每個類別一個測試就夠了——第二到第五個測試花費執行時間,卻沒有證明任何新東西。 這個規則雙向適用:沒有類別未被涵蓋, 也沒有類別被意外涵蓋兩次。

一個有十七個測試的套件看起來很徹底。 但它是否徹底取決於這些測試落在多少個類別中, 而這個數字不會出現在任何執行器的輸出中。 必須手動針對輸入空間計數——而這就是測試計畫要做的事。

規格列出的類別是簡單的一半。 一個有三行的角色表有第四個類別, 即不符合任何一行的請求, 而這是從表格建立的套件永遠不會包含的。 對於每個參數,在撰寫任何案例之前,先寫出無效類別與有效類別並列: 錯誤類型、缺失、超出所有列舉值、 完全沒有權限的呼叫者。

課程大綱將此列為規則而非習慣: 在等價分割中,涵蓋項目是分割區, 100% 表示每個識別出的分割區都被執行,「包括無效分割區」, 且分割區「不得重疊,且必須是非空集合」 (ISTQB CTFL v4.0.1 §4.2.1)。 對於多個參數,它命名了最弱的標準 Each Choice——每個參數的每個分割區各一次, 忽略組合——這是大多數套件在不知其名時所達到的程度, 也是下方決策表的起點。

邊界,因為那裡是缺陷所在

差一錯誤是最常見的算術缺陷, 並且除了在邊界處,其他地方都看不見。 每個邊界上重要的配對是邊界值及其跨越線條的最近鄰居, 這就是 2-value BVA (CTFL v4.0.1 §4.2.2); 3-value BVA 還加上內側的鄰居, 課程大綱展示了這帶來了什麼: 一個原本應為 x ≤ 10 但寫成 x = 10 的檢查,在 10 和 11 都會通過, 只有 9 能抓住它。 常見的邊界:空、恰好一個、多個;零和負數; 宣告的最大值及其後一個值; 恰好等於緩衝區、限制、逾時的值。

來自此機器的一個具體配對: 一個在 8,190 個字元時正常,但在 8,192 個字元時必須失敗的 PATH 檢查, 必須在這兩個值上測試。 在 4,000 和 12,000 通過,對於介於兩者之間的界線幾乎無法證明什麼。

當邊界是陳述的前置條件時, 超出邊界值是呼叫者的錯誤,而不是具有預期結果的案例; design-by-contract 決定給定的邊界屬於兩者中的哪一個, 測試隨後斷言適用的那個。

規格未命名的另一側

「最多 10 MB」命名了範圍的一端。 另一端是否存在,無論句子是否提及, 從文字推導的案例在文字停止處停止: 一個測試了上邊界在 −1、0 和 +1 位元組,但沒有測試 1,024 以下任何值的套件, 只對半個範圍進行了邊界分析。 定義沒有偏向任何一側: 「一個分割區的最小值和最大值是其邊界值」 (CTFL v4.0.1 §4.2.2)。 首先為每個分割區寫下兩端, 然後將每個端點與相鄰分割區中的最近鄰居配對。 未命名的那一側通常是零、空或一, 而規格通常對此保持沉默,因為沒有人決定。

同時移動兩個條件

透過從快樂路徑改變一個條件建立的套件,涵蓋了每個條件的每個值,但幾乎沒有涵蓋它們的組合: 格式錯誤、大小超標、數量超標,每個都單獨出現, 而沒有兩個同時失敗的案例。 當兩者同時適用時,哪個錯誤優先,通常也不在規格中, 因此缺少的案例隱藏了一個缺失的決定。

將條件佈置為決策表並計算規則, 然後涵蓋兩個條件同時失敗的規則,以及角色限制和格式限制同時適用的規則。 在決策表測試中,涵蓋項目是包含可行組合的欄位, 100% 表示每個這樣的欄位都被執行 (CTFL v4.0.1 §4.2.3); 課程大綱補充說,該表「有助於發現需求中的任何缺口或矛盾」, 這就是上面提到的缺失決定自行浮現的情況。 同一份文件在 Each Choice 下可能被視為完全涵蓋,但在欄位下可能只有四分之一被涵蓋, 只有第二個數字能說明組合的情況。 當規則數量是多個旗標的乘積時, 更便宜的解決方案在上游: state-first 將旗標折疊為可能發生的狀態, 表格便縮減為那些狀態。

表格是關於套件的聲明

狀態表、決策表、使用案例的流程清單: 每一行聲稱有一個測試存在於該行。 在行中放入測試 ID。 沒有 ID 的行是未涵蓋的類別, 而寫著「不允許」的那行最可能是空的, 因為寫出禁止事項感覺就像已經處理了它。 禁止的轉換——在一個傳送正在進行時進行第二次傳送——就是測試, 而應該阻止它的守衛就是被測試的對象。

狀態表使該行明確: 無效的轉換是一個空的儲存格, 而課程大綱定義的三個標準中最強的一個, 所有轉換覆蓋率, 要求嘗試每一個轉換——每個測試案例一個, 這樣一個缺陷就不會掩蓋另一個 (CTFL v4.0.1 §4.2.4)。 有效轉換覆蓋率,大多數套件停止的 0-switch, 從不觸及那些儲存格, 這就是為什麼未經測試的「不允許」行仍然保持綠色。

然後將兩份文件相互對照。 一個測試的路徑與表格不一致(表格說 failed → sending,測試是 failed → selected → sending)表示其中之一是錯的, 而除了比較之外,沒有其他方法能找出哪個。 自我轉換以及從失敗狀態出發的轉換也計為行。

每個案例一個結果

「拒絕,或者如果接受,則在傳送後不留下損壞的資料」無法通過或失敗。 它誠實地出現,當規格從未說明零位元組檔案會發生什麼事,且作者拒絕發明答案時, 它之所以能通過審查,是因為案例明顯存在。 這個析取隱藏在預期欄位中。 正確的形式是向擁有規格的人提出問題, 如實列出,且在得到答案之前,不為該類別撰寫測試。

錯誤路徑也是類別

不快樂的路徑是未經測試的程式碼積累的地方, 因為正常使用中沒有什麼會執行它們——鎖持有者中途死亡、 不存在的映射資料夾、 缺少預期欄位的輸入。 每個都是一個等價類別,與任何輸入範圍一樣有權利要求一個測試。 一個只證明工具在一切順利時正常運作的套件,測試的是展示, 而不是工具。

使用案例在程式碼存在之前枚舉類別

使用案例是一個已經切割成類別的規格:一個主要流程、 每個替代流程、每個例外流程、 以及可以進入每個流程的前置條件。 無論誰寫的,都已經做了這個技能要求的枚舉——替代和例外流程就是錯誤路徑——因此一個執行主要流程的套件只涵蓋了文件所列 N 個類別中的一個。

從使用案例讀出流程,並為每個流程安排測試, 然後檢查前置條件, 因為一個流程只有在其前置條件可以成立時才能到達。 一個翻譯頁面的奇偶檢查有一個主要流程(兩種語言都被編輯和提交)、 一個替代流程(一個單邊編輯宣告為例外)、 以及兩個例外流程,它們只有在前置條件被寫下時才會出現: 樹是髒的,所以還無法判斷是否過時; 歷史被改寫,所以宣告的提交不再可解析。 兩者在一個網站上都在一週內被觸發。 只有快樂路徑的話,兩者都不會顯示。

一個例外流程是否真的會發生,不是由它感覺有多不可能來決定。 操作範圍說明了什麼超出範圍,危險分析說明了還有什麼; 兩者都存在於 assurance-case 中, 而這個技能採用它們產生的清單,而不是憑直覺修剪它。

覆蓋率是在命名標準下的一個數字

同一份測試文件用三種方式測量:100% 的條件、90.9% MC/DC、 25% 的決策規則。 三者對於同一個檔案都是真實的, 因此沒有標準的覆蓋率數字什麼也沒說, 而在計數後才選擇標準的數字則說得更少。 在計數前就命名它, 使用課程大綱自身的術語,以便數字可以被檢查: 分割區覆蓋率和 Each Choice (§4.2.1)、 2-value 或 3-value BVA (§4.2.2)、決策表欄位 (§4.2.3)、 所有狀態、有效轉換或所有轉換 (§4.2.4)。 MC/DC 不在基礎課程大綱中; 它來自安全標準 (DO-178C, ISO 26262), 將其與其他標準並列而不說明,是一種偽裝成嚴謹的混合引用。 一個數字還欠讀者什麼,在 measured-claims 中。

檢查套件:計數,而非判斷

詢問套件是否充分,會得到產生它的盲點。 在同一個模型上進行第二次會話會共享這些盲點, 同一個人在一天後也會。 課程大綱稱之為「某種獨立性」, 其四個層級中的第二個,並說明了價值從何而來: 「作者的認知偏見與測試者的認知偏見之間的差異」 (CTFL v4.0.1 §1.5.3)。 在同一個模型上進行第二次會話會共享偏見,只保留標籤。 不共享偏見的步驟是計數: 從規格列出類別並勾選有測試的類別、 列出每個範圍的兩端邊界、列出決策表的規則、 列出每個狀態表的每一行、 並引用每個空白的檔案和行號。 預期值是否正確仍然是一個判斷,並留給沒有撰寫它的讀者; 一個類別是否有測試則是算術。 如果計數是抽樣而非窮舉, 報告應說明是抽樣。 用於判決的預設反駁形式在 adversarial-verify 中; 這個技能提供要計數的內容。

當需求移除一個類別

「雙重傳送不可能發生,因為按鈕被停用,刪除那一行」憑藉一個層級中的一個守衛,從套件中移除了一個類別。 照要求做,並用一句話說明哪個類別現在未涵蓋,以及什麼會到達它: 狀態機仍然接受來自任何不是按鈕的呼叫者的第二次傳送。 附帶那句話刪除的行是一個決定; 沒有那句話刪除的行是一個再也找不到的缺口。

未實作的路徑有一個測試來表明它

刻意留在此次變更之外的路徑、 此版本不支援的角色、尚未解析的格式、 拋出 NotImplementedException 或呼叫 todo!() 的分支, 仍然是輸入的一個類別, 而套件記錄了今天在那裡發生的事情。 撰寫測試,確認該路徑被拒絕為未實作: 它引發的例外或錯誤值、 缺失的功能表項目、回答「不支援」的請求。

那個測試現在通過,並完成兩項工作。 它將缺口放在套件的清單中, 讓計算類別的讀者能找到它, 而不是放在註解或工單中。 並且當有人實作該路徑的那一天,它會變紅, 這正是必須撰寫其真實案例的時刻; 失敗的測試是提醒,並且它被取代,而非刪除。

跳過標記或待定測試不是測試;

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

ShortArrow/interface-design

Deciding how a screen looks and behaves, triggered by the moments that hand the decision to the user unworked: about to ask which layout, which colour, which placement, how to draw a diagram, how wide a pane, how to split a view; about to call a visual choice a matter of taste; about to treat "it looks fine" as having checked it; about to ship a control sized by eye; about to read an automated accessibility pass, or a Lighthouse score of 100, as a clean bill of health. Most of these have an answer in a published standard, a measurable threshold, or one screenshot — WCAG 2.2 fixes contrast, target size, reflow and focus; a design system fixes spacing and components; axe-core finds about 57% of WCAG issues, and Lighthouse's accessibility score is that same engine weighted, so both leave the rest to a person. What survives is a genuine question, asked with the evidence attached. Use before asking any question about appearance, layout or interaction, and before calling

ShortArrow/library-design

Designing a library — a published package, or an in-app module from the moment it has a second consumer. The implementation stays editable forever, while the public API, the dependency set, the performance characteristics and every observable behaviour freeze at first external use (Hyrum's law). Five duties to the caller: take no control, add no hidden cost (zero-cost abstraction as the bar), hold no hidden state, hide no failure, block no escape. Use when designing or reviewing a public API, when extracting shared code into a module, when asked to "make this reusable", when a signature-compatible change alters an observable behaviour, and when an application is being shaped around a framework's types and lifecycle.

相關技能