🎯 什麼情境該想到我
當你「大部分錯誤都要等到整合測試甚至上線才被抓到,而測試跑一輪要好幾小時」的時候。
⚙️ 怎麼用(步驟 / 公式)
意圖:讓自動化測試套件盡可能早地找到錯誤——最快的測試先跑,並讓大多數錯誤能被單元測試抓到,把回饋從「四小時」壓回「十分鐘」。
三層定義(由快到慢)
- Unit tests 單元測試:測試單一 method、class 或 function,在隔離狀態下驗證程式如設計般運作。為了保持快速與無狀態,單元測試常把資料庫與其他外部相依「stub out」(改成回傳靜態預設值,而不是真的呼叫資料庫)。
- Acceptance tests 驗收測試:把應用程式當作整體來測,確保更高層次的功能如設計般運作(user story 的業務驗收條件、API 的正確性),並確保沒有引入回歸錯誤。Humble 與 Farley 的區分:「The aim of a unit test is to show that a single part of the application does what the programmer intends it to…The objective of acceptance tests is to prove that our application does what the customer meant it to, not that it works the way its programmers think it should.」通過驗收測試的 build 才交付手動測試(探索性測試、UI 測試)與整合測試。
- Integration tests 整合測試:確保應用程式與其他生產應用與服務正確互動,而不是呼叫 stub 出來的介面。因為整合測試往往很脆弱(brittle),我們要盡量減少整合測試的數量,把缺陷盡可能在單元與驗收測試階段找出來。能在跑驗收測試時使用遠端服務的虛擬或模擬版本,是一項必要的架構需求。
兩個具體門檻
- 測試涵蓋率閘門:把測試涵蓋率(以類別數、程式行數、排列組合數等為函數)量測並可視化,甚至當涵蓋率低於某個水準時讓驗證測試套件失敗(例如低於 80% 的 class 有單元測試時)。
- Martin Fowler 的十分鐘 build:第一階段做編譯並跑較侷限的單元測試、資料庫完全 stub 掉,這類測試跑得非常快,守得住十分鐘準則;但牽涉大規模互動、尤其是真實資料庫的 bug 找不到。第二階段 build 跑另一套(驗收)測試,會打真實資料庫、涵蓋更多端到端行為,這套可能要跑上幾個小時。
規則:每當用驗收測試或整合測試找到一個錯誤,就建立一個能更快、更早、更便宜找到它的單元測試。Martin Fowler 稱這個形狀為「ideal testing pyramid」——大多數錯誤用單元測試抓到;許多測試計畫則是倒過來的,大部分投資都在手動與整合測試。
最重要的診斷判準:如果你發現單元或驗收測試太難寫、太貴維護,那很可能代表架構太緊耦合,模組邊界之間已經(或從來沒有)強分離。此時該做的是讓系統更鬆耦合,讓模組能不靠整合環境獨立被測——而不是繼續硬寫測試。即使是最複雜的應用,能在幾分鐘內跑完的驗收測試套件也是做得到的。
平行化
- 測試要設計成可平行跑,甚至跨多台不同伺服器;不同類別的測試也可平行(例如 build 通過驗收測試後,效能測試與安全測試平行跑)。
- 通過所有自動化測試的 build 才開放給探索性測試與其他手動/資源密集測試(如效能測試),並盡可能頻繁進行(持續或排程)。
- 任何測試者(包含所有開發者)都應該使用「最新一個通過全部自動化測試」的 build,而不是等開發者標記某個 build 可以測,這樣測試才會盡早發生。
測試環境本身也要測
- 許多非功能需求(可用性、擴充性、容量、安全等)其實靠環境的正確組態達成,所以要建自動化測試驗證環境確實被正確建置與組態:支撐用的應用/資料庫/函式庫、語言直譯器與編譯器、作業系統(例如已啟用稽核日誌)、所有相依。
- 使用 infrastructure as code 工具(Puppet、Chef、Ansible、Salt、Bosh)時,可以用測程式碼的同一套測試框架來測環境(例如把環境測試寫成 cucumber 或 gherkin 測試)。
- 也要對「建構環境的程式碼」跑分析工具(Chef 的 Foodcritic、Puppet 的 puppet-lint),並把安全強化檢查(server-spec)納入自動化測試。
原文:「Therefore, whenever we find an error with an acceptance or integration test, we should create a unit test that could find the error faster, earlier, and cheaper.」(L5324–5325)
原文:「If we find that unit or acceptance tests are too difficult and expensive to write and maintain, it’s likely that we have an architecture that is too tightly-coupled」(L5334–5335)
原文:「Any tester (which includes all our developers) should use the latest build that has passed all the automated tests, as opposed to waiting for developers to flag a specific build as ready to test.」(L5366–5368)
🧪 我實際套用的紀錄
- (待填)
⚠️ 注意 / 什麼時候不適用
- 涵蓋率門檻(如 80%)是防止交期壓力下悄悄停寫單元測試的偵測手段,不是品質本身;把它當 KPI 追會養出無意義的測試。
- 測試多不等於測試好:見只自動化可信的測試,不可靠的自動化測試會讓整套結果被忽略。
- 若單元測試難寫,先改架構而不是加測試——這條是書中的診斷訊號,不是可有可無的建議。
- 允許「還沒跑完全部自動化測試就開放手動探索測試」能加快回饋,但也可能讓人測到最終會失敗的 build,這是明確的取捨。
🔗 相關工具
- 只自動化可信的測試(測試的可信度比數量更重要,是這條的必要配套)
- 虛擬安燈繩(測試紅了之後怎麼處置)
- 演進式架構(「架構太緊耦合」的處方)
- 工具-部署管線與持續交付(上位心法卡,測試金字塔是管線的內容物)
- 工具-紅綠重構循環(單元測試層的日常節奏)
- 工具-測試先行的設計(先寫測試迫使模組邊界鬆耦合)
- 工具-特徵測試(既有系統沒有測試時的切入手段)
- DevOps手冊