K6
在現代網頁與 API 系統開發中,效能測試是確保系統穩定性的關鍵環節。透過系統性的測試,我們能提前發現潛在瓶頸,確保系統在高併發情況下仍能穩定運作。
什麼是壓力測試?
壓力測試(Stress Testing)是一種軟體測試方法,主要目的是評估系統在極端條件下的穩定性與可靠度。這些極端條件包括:
- 高流量訪問
- 高併發請求
- 大量資料處理
透過壓力測試,我們可以:
- 找出系統的最大承載能力
- 識別潛在瓶頸點
- 觀察系統在超出設計負載時的行為表現
- 確保系統能優雅地處理錯誤情況
常見應用情境
- 電商網站:促銷活動期間的高流量衝擊
- 金融系統:結算時段的高併發交易處理
- API 服務:短時間內大量請求的處理能力
測試類型分類
根據測試目的和強度,可細分為以下幾種類型:
- Smoke Testing(煙霧測試):驗證系統在低配備、正常負載下的基本運作
- Load Testing(負載測試):測量系統在一般及尖峰負載下的效能指標
- Stress Testing(壓力測試):測試系統在高負載或極端條件下的穩定性
- Spike Testing(尖峰測試):模擬瞬間流量驟升的系統反應
- Soak Testing(浸泡測試):測試系統長期運作下的穩定性
安裝與環境設置
使用 Docker 執行 K6
推薦使用 Docker 執行 K6,優點包括:
- 避免複雜的依賴安裝
- 確保跨平台的一致執行環境
- 簡化部署流程
前置需求:
- 已安裝 Docker
- 準備測試腳本(JavaScript)
基礎測試腳本範例
// test.js
import http from "k6/http";
import { check, sleep } from "k6";
export const options = {
vus: 10, // 10 個虛擬使用者
duration: "30s", // 測試持續 30 秒
};
export default function () {
const res = http.get("https://test-api.k6.io");
check(res, {
"status is 200": (r) => r.status === 200,
});
sleep(1); // 每次請求間隔 1 秒
}
執行測試
將上述腳本儲存為 test.js,透過以下指令執行:
docker run --rm -i -v ${PWD}:/scripts -w /scripts grafana/k6 run test.js
指令說明:
--rm:測試結束後自動移除容器-i:允許 Docker 接受 stdin 輸入-v ${PWD}:/scripts:將當前目錄掛載到容器內的/scripts路徑-w /scripts: 指定資料夾為 scriptsgrafana/k6:使用官方 K6 Docker 映像檔run /scripts/test.js:執行掛載的測試腳本
測試腳本撰寫技巧與實例
K6 使用 JavaScript 撰寫測試腳本,語法直觀且功能強大。以下提供常見情境的實作範例:
1. 基本 GET 請求測試
export default function () {
http.get("https://example.com");
}
2. API 回應內容驗證
const res = http.get("https://example.com/api/user");
check(res, {
"狀態碼為 200": (r) => r.status === 200,
"回應時間 < 1 秒": (r) => r.timings.duration < 1000,
"回傳內容包含 userId": (r) => r.body.includes("userId"),
});
3. 模擬使用者登入(POST 請求)
const payload = JSON.stringify({
username: "admin",
password: "123456",
});
const params = {
headers: { "Content-Type": "application/json" },
};
const res = http.post("https://example.com/api/login", payload, params);
check(res, {
登入成功: (r) => r.status === 200,
});
4. 使用 setup() 函數共享資料
export function setup() {
// 在測試開始前執行,回傳的資料會傳遞給所有虛擬使用者
return { token: "abc123" };
}
export default function (data) {
const res = http.get("https://example.com/api/data", {
headers: { Authorization: `Bearer ${data.token}` },
});
check(res, {
取得資料成功: (r) => r.status === 200,
});
}
5. 解析 HTML 內容
import http from "k6/http";
import { parseHTML } from "k6/html";
export default function () {
const loginPage = http.get("https://example.com/login");
const doc = parseHTML(loginPage.body);
const csrfToken = doc.find('input[name="_token"]').first().attr("value");
// 使用解析出的 CSRF token 進行後續操作
const payload = JSON.stringify({
username: "admin",
password: "123456",
_token: csrfToken,
});
const params = {
headers: { "Content-Type": "application/json" },
};
const res = http.post("https://example.com/login", payload, params);
check(res, {
登入成功: (r) => r.status === 200,
});
}