E2E 測試天生就愛壞,壓力測試讓你睡得好

一句話總結:E2E 測試只測最值錢的流程,壓力測試告訴你系統在哪個點會斷裂。

結論先講:如果你用 E2E 測試覆蓋所有情境,你會花 80% 的時間在修 flaky test 而不是寫功能。壓力測試則是你上線前最不想省的一步。

E2E 測試:模擬真人操作

E2E 測試做的事情很直觀——開一個真的瀏覽器(或 headless browser),模擬使用者點擊、輸入、跳轉,看整個流程走不走得通。

聽起來很美好?問題是 E2E 測試有幾個天生的痛點:

  • :一個測試跑幾秒到幾十秒是常態
  • 脆弱:DOM 結構一改、動畫時間一變、網路稍微慢一點,測試就壞了
  • 定位模糊:測試失敗時,你不知道是前端壞了、後端壞了、還是資料庫壞了

所以 E2E 測試的策略是:只測最關鍵的流程,不要貪多。

什麼叫「最關鍵的流程」?就是那些壞掉的話會直接影響營收或使用者信任的路徑:

  • 註冊 → 登入 → 核心操作(下單、付款、發文…)
  • 關鍵的 Happy Path
  • 跨系統的端到端驗證

其他的邊界條件、異常路徑?交給 Unit Test 和 Integration Test 去處理。

E2E 測試的存活指南

寫 E2E 測試最怕的就是 flaky——同一份測試有時過有時不過,沒改任何 code。幾個減少 flaky 的做法:

  • 不要用 sleep(3000) 這種硬等,用 waitForSelectorcy.intercept 等動態等待
  • 測試資料要獨立——每個測試用自己的資料集,不要共享
  • 執行環境要穩定——專用的測試環境,不要跟開發環境搶資源
  • 如果你非得用 retry,那是止血,不是治療。記得回頭找根因

工具怎麼選?

  • Cypress:開發體驗最好,自動等待機制、時間旅行除錯都很強。但只支援 Chromium 系瀏覽器是個限制
  • Playwright:跨瀏覽器支援(Chrome、Firefox、Safari),自動程式碼生成,微軟出品。需要跨瀏覽器測試的話選它
  • Selenium:最老牌,支援最多語言。但開發體驗相對差,通常是 legacy 系統或有特殊需求才用

我的建議?新專案直接選 Playwright 或 Cypress。如果團隊已經在用 Selenium 而且跑得好好的,不用為了潮而換。

壓力測試:找出系統的天花板

壓力測試回答的問題很實際:我們的系統能撐多少人同時用?瓶頸在哪?

如果你跳過壓力測試就上線,那你就是拿你的使用者當白老鼠。運氣好沒事,運氣不好上線第一天系統崩潰,公司公關危機、使用者流失、老闆的眼神——你不會想體驗的。

四種測試類型

不是所有壓力測試都一樣,看你想驗證什麼:

  • Load Test:模擬正常流量,確認系統穩定。「我們預期的日常負載扛不扛得住?」
  • Stress Test:逐步加壓直到系統崩潰。「系統的極限在哪?瓶頸是 CPU、記憶體還是資料庫?」
  • Spike Test:瞬間灌入大量請求。「搶票、閃購那種場景撐得住嗎?」
  • Soak Test:中等負載長時間跑。「跑 24 小時會不會 memory leak?」

關鍵指標

壓力測試看什麼數字?

  • 回應時間:P50(中位數)、P95、P99 的延遲分佈。P99 才是真正會咬你的那個數字
  • 吞吐量(QPS / RPS):每秒處理多少請求
  • 錯誤率:非 2xx 回應的比例。超過 5% 就要認真看了
  • 資源使用率:CPU、Memory、Disk I/O、Network

實戰範例:k6 壓力測試

k6 是我最推薦的壓力測試工具——用 JavaScript 寫腳本、輕量、CI 友善。來看一個實際的腳本:

import http from 'k6/http';
import { check, sleep, group } from 'k6';
import { Rate, Trend } from 'k6/metrics';
 
const errorRate = new Rate('errors');
const orderTrend = new Trend('order_creation_duration');
 
export const options = {
  stages: [
    { duration: '1m', target: 50 },   // 慢慢爬到 50 人
    { duration: '3m', target: 50 },   // 維持 50 人觀察穩定性
    { duration: '1m', target: 100 },  // 加壓到 100 人
    { duration: '3m', target: 100 },  // 維持觀察
    { duration: '2m', target: 0 },    // 緩慢降回
  ],
  thresholds: {
    http_req_duration: ['p(95)<500', 'p(99)<1000'],
    errors: ['rate<0.05'],
    order_creation_duration: ['p(95)<800'],
  },
};
 
export default function (data) {
  const headers = {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${data.token}`,
  };
 
  group('瀏覽商品', () => {
    const res = http.get(`${BASE_URL}/api/v1/products?page=1`, { headers });
    check(res, { '回傳 200': (r) => r.status === 200 });
    errorRate.add(res.status !== 200);
    sleep(1);
  });
 
  group('建立訂單', () => {
    const payload = JSON.stringify({
      items: [{ productId: 'prod-001', quantity: 1 }],
    });
    const res = http.post(`${BASE_URL}/api/v1/orders`, payload, { headers });
    check(res, { '建立成功': (r) => r.status === 201 });
    errorRate.add(res.status !== 201);
    orderTrend.add(res.timings.duration);
    sleep(2);
  });
}

注意 thresholds 的設定——這是你跟系統之間的合約:P95 回應時間必須低於 500ms,錯誤率必須低於 5%。k6 跑完會直接告訴你有沒有違約。

其他壓力測試工具

  • Artillery:YAML 配置,Node.js 生態系,支援 WebSocket
  • JMeter:GUI 操作,功能最豐富,但學習曲線陡
  • Locust:Python 寫腳本,分散式支援好,Python 團隊首選

這篇的重點回顧

E2E 測試只測最值錢的流程,用動態等待取代硬等,工具選 Playwright 或 Cypress。壓力測試用 k6 定義效能合約,四種測試類型對應四種不同的問題。

下一篇是這個系列的最終章——怎麼把這些測試整合進 CI/CD pipeline,以及測試過程中最常見的坑。

系列文章:

延伸閱讀:

「壓力測試就像體檢——你不做它不代表沒病,只是你不知道而已。」