~/VibeHandbook
免費 PDF

15 · 02

測試:你的安全網

測試就是用來檢查其它程式碼是否如你預期那樣執行的程式碼。一旦寫好,它會在幾秒內執行完,並立即告訴你是否有東西回退(regression)了。

一套健康的測試套件長得像一座金字塔:底座是大量又小又快的單元測試,中間是較少的整合測試,頂端只有一小撮緩慢的端到端測試。你的大部分安全感,都廉價地來自底座:

              ╱╲
             ╱  ╲        E2E         少 · 慢 · 整個應用    
            ╱────╲       (端到端)    
           ╱      ╲
          ╱  INTEG ╲     INTEGRATION  若干 · 中等  
         ╱──────────╲    (各部件組合)    
        ╱            ╲
       ╱     UNIT     ╲  UNIT          多 · 快 · 單個函式        
      ╱────────────────╲
         廉價的底座 = 大部分測試

你不需要 100% 的覆蓋率。先從那些一旦壞掉就最傷的東西開始測:登入、支付、無法重新生成的資料、核心業務邏輯。AI 非常擅長寫測試——往往比寫功能還要強,因為預期行為是具體明確的。

下面這個 prompt 效果很好:

Write tests for the calculateDiscount function in src/pricing.js.
Cover: a normal case, the zero/empty case, the maximum-discount
boundary, and one invalid input. Use the existing test setup in
this repo (check how other *.test.js files are structured).
Run the tests and show me they pass before you finish.

最後那一行很關鍵。一定要讓 AI 執行測試,而不只是寫出來。一個從未被執行過的測試只是一個猜測。具體說明要覆蓋哪些情況也很有價值,因為放任不管的話,AI 傾向於寫出三種 happy path 的變體,然後就聲稱很周全了。真正能抓出 bug 的,是那些無聊的情況:空輸入、邊界值、本應丟擲異常的東西。

它應當產出的東西的一個簡單示例:

import { calculateDiscount } from "./pricing.js";
import { describe, it, expect } from "vitest";

describe("calculateDiscount", () => {
  it("applies a normal percentage discount", () => {
    expect(calculateDiscount(100, 0.2)).toBe(80);
  });

  it("returns the full price when discount is zero", () => {
    expect(calculateDiscount(100, 0)).toBe(100);
  });

  it("never discounts below zero", () => {
    expect(calculateDiscount(100, 1.5)).toBe(0);
  });

  it("rejects a negative price", () => {
    expect(() => calculateDiscount(-10, 0.2)).toThrow();
  });
});

當你之後改動某個功能時,先執行測試。如果它們變紅了,你就在使用者之前發現了破綻。當 AI 修復一個 bug 時,讓它補一個針對舊行為會失敗的測試——這樣這個 bug 就不會悄無聲息地捲土重來。這個習慣有個名字:迴歸測試(regression test)。你遇到的每一個 bug,都是程式碼庫付出代價換來的教訓;迴歸測試就是你保住這個教訓的方式。

一個提醒:測試本身也可能是錯的。如果 AI 寫了一個斷言有 bug 的行為是正確的測試,它會永遠通過,而你的應用會一直壞著。所以要讀斷言,而不只是看綠色對勾。一個通過的測試只意味著"程式碼做了測試所說的事"——你仍然得確認這個測試說的是對的事。

想離線閱讀?

免費下載整本書的 PDF 或 EPUB。