測試程式碼和產品程式碼一樣重要——骯髒的測試比沒有測試更糟,因為它會隨系統演進而腐爛,最後成為改動的絆腳石。整潔測試只有一個衡量標準:可讀性。一個好測試該像一則小故事,讓人一眼看穿「它在測什麼、期待什麼」。
🧠 Why It Matters
- 測試是重構的勇氣來源:有高覆蓋率又整潔的測試,你才敢改程式碼——因為任何錯誤都會被立刻抓到。測試腐爛了,這份勇氣也跟著消失。
- AAA / Build-Operate-Check 結構:每個測試分三段——Arrange(建好資料與初始狀態)、Act(呼叫目標)、Assert(驗證結果)。三段分明,讀者掃一眼就知道故事的起承轉合。
- 一個測試只測一個概念:斷言數量要少,更精確地說是「只驗證一件事」。把「新增/修改/刪除」串在同一個測試裡,一旦中間失敗,你無法第一時間判斷是哪一環出事。
- 自我驗證,不靠肉眼:測試結果只有 Pass / Fail,不該要求人工比對輸出——否則測試就退化成「跑一次看看」的手動勞動。
NOTE
FIRST 五律:Fast(夠快才會常跑)、Independent(測試互不依賴,才能精準定位失敗)、Repeatable(任何環境都能重跑)、Self-Validating(只回傳 Pass/Fail)、Timely(在產品碼之前寫,即 TDD)。
⚖️ Case Study
前 — 一個測試測了三件事,結構糊成一團
test("cart works", () => {
const cart = new Cart();
cart.addItem({ name: "Apple", price: 10 });
expect(cart.total()).toBe(10); // 測了「新增」
cart.removeItem("Apple");
expect(cart.total()).toBe(0); // 又測了「移除」
cart.applyCoupon("SAVE5");
cart.addItem({ name: "Book", price: 20 });
expect(cart.total()).toBe(15); // 還測了「折價券」
});名字 cart works 什麼都沒說;中間任一 expect 失敗,你都得回頭讀整段才知道壞在哪。三個概念擠在一個測試裡。
後 — 一概念一測試,AAA 三段分明
test("adding an item increases the total by its price", () => {
const cart = new Cart(); // Arrange
cart.addItem({ name: "Apple", price: 10 });
const total = cart.total(); // Act
expect(total).toBe(10); // Assert
});
test("removing the only item brings the total back to zero", () => {
const cart = new Cart();
cart.addItem({ name: "Apple", price: 10 });
cart.removeItem("Apple");
expect(cart.total()).toBe(0);
});
test("a coupon discounts the total by its value", () => {
const cart = new Cart();
cart.addItem({ name: "Book", price: 20 });
cart.applyCoupon("SAVE5");
expect(cart.total()).toBe(15);
});每個測試名字就是一句規格,失敗時直接告訴你哪個概念壞了;三段 Arrange-Act-Assert 讓故事一目了然。
WARNING
別為了「省事」把斷言全塞進一個測試。測試的價值在失敗時能不能立刻定位問題;一個測十件事的測試失敗時,等於什麼都沒告訴你。
🔑 Takeaways
- 骯髒測試比沒測試更糟——測試碼要和產品碼一樣整潔,衡量標準是可讀性。
- 用 AAA / Build-Operate-Check 三段結構,讓每個測試像一則小故事。
- 一個測試只測一個概念,失敗時才能精準定位。
- 遵守 FIRST:快、獨立、可重複、自我驗證、即時。
- 整潔測試是重構勇氣的來源,與 紅-綠-重構 互為表裡。
No notes yet — jot your takeaways or Q&A here.