一、兩週後的資料夾現場
打開 perf-testing 看一眼:first-test.js、pizza-api-test.js、from-curl.js、login-flow.js、journey.js、mixed.js、shape-load.js、shape-spike.js——兩週累積了八支腳本。它們都能跑,但已經開始生病:

圖 1:三種病——重複的程式碼、寫死的值、三個月後看不懂
最明顯的是登入流程:login-flow.js、journey.js、mixed.js 各自帶著一份幾乎相同的註冊加登入程式碼。哪天 API 的欄位改了,你要改三個地方——而總有一處會漏。網址也一樣:https://quickpizza.grafana.com 散落在每支腳本裡,之後要對公司環境跑,得逐檔搜尋替換。這些病在八支腳本時只是不便,到八十支時就是災難。腳本是資產還是負債,差別只在維護性——今天做的就是把它們留在資產這一邊。
二、整理的三條紀律
不需要搬整套軟體工程,測試腳本的整理守住三條就夠:
• 單一事實來源:同一件事(登入流程、目標網址)只寫在一個地方,其他人引用它。改一處,處處生效
• 設定與邏輯分離:會隨環境變的(網址、帳密、人數)放設定,不變的行為放邏輯。換環境只動設定
• 命名講人話:檔名讓人一眼知道用途——journey-order.js 比 test2-final(1).js 誠實一百倍
接下來的三個動手做,就是把三條紀律落在自己的資料夾上。
三、動手做一:環境變數切換目標
Day 8 用環境變數藏機密,今天用它切換環境。目標:同一支腳本,不改任何程式碼,就能對練習站或公司測試環境執行:
Prompt 1|讓網址可切換
請修改 journey.js:把寫死的 https://quickpizza.grafana.com
改成從環境變數 BASE_URL 讀取,沒有提供時預設仍用練習站。
只改這件事,其他不動。改完告訴我兩種執行方式的指令。
// 腳本開頭:有給就用給的,沒給就用預設(練習站)
const BASE = __ENV.BASE_URL || 'https://quickpizza.grafana.com';
# 平常練習:什麼都不加,打練習站
k6 run journey.js
# 之後在公司:一個參數切到 staging(記得先走 Day 6 五問)
k6 run -e BASE_URL=https://staging.example.com journey.js
這一小步的意義比看起來大:從此「腳本」與「環境」解耦,練習寫的每一支,換個參數就是公司可用的資產。
四、動手做二:把登入抽成共用模組
三支腳本各自帶登入碼的病,用「抽成模組」根治。k6 支援標準的模組匯入——把登入寫成一個檔案,大家引用它:

圖 2:整理前各自為政,整理後共用一份事實——改一處,全部受益
Prompt 2|抽取共用模組
請做以下重構,一步一步來:
1. 建立 lib/auth.js:把「註冊唯一帳號並登入取得 token」的流程
抽成函式 registerAndLogin(baseUrl),回傳 token,並匯出(export)。
2. 修改 login-flow.js 與 journey.js:改成 import 這個函式,
刪除各自重複的登入碼。
行為必須完全不變。每改完一支就停下來,等我跑過測試再繼續。
模組與引用的樣子:
// lib/auth.js — 登入流程的唯一事實來源
import http from 'k6/http';
import { check } from 'k6';
export function registerAndLogin(baseUrl) {
const username = `perf_${Date.now()}@test.example.com`;
const password = 'Perf_12345678';
/* ...註冊與登入,同 Day 10... */
return login.json('token');
}
// journey.js — 引用,不再自己寫
import { registerAndLogin } from './lib/auth.js';
export function setup() {
return { token: registerAndLogin(BASE) };
}
prompt 裡那句「每改完一支就停下來,等我跑過測試再繼續」是刻意的——它引出本篇最重要的工作方式。
重構的安全網:行為不變,用測試作證

圖 3:小步改、跑 smoke、看 checks——綠了才走下一步,紅了就退回
整理(重構)的定義就是只搬家、不改行為。行為沒變的最好證據,就是你的測試:每完成一小步,用 1 個 VU 跑一次該腳本,checks 全綠代表搬家成功;紅了就退回這一步找原因。這對 QA 應該似曾相識——這正是「重構要有測試保護」的實踐,只是這次被保護的對象是測試腳本自己。有了安全網,「改一改全壞了」這種恐懼就不存在:最壞的情況也只是退回上一步。
五、動手做三:請 AI 提整理計畫,你來審
前兩項是我指定的整理。真實情境裡,該整理什麼要先盤點——這件事 AI 做得又快又好,但決定權在你:
Prompt 3|先要計畫,不要動手
請盤點這個資料夾的所有 .js 腳本,列出:
1. 重複出現的程式邏輯(在哪幾支、各在哪一段)
2. 寫死的值(網址、人數、路徑)與建議的處理方式
3. 建議的整理順序:從風險最低的開始,一次一步
先給我計畫就好,不要修改任何檔案。
拿到計畫後照三個標準審:每一步夠小嗎(一步只做一件事)?順序合理嗎(低風險先行)?有沒有過頭的建議(見注意事項的「過度工程」)?審完挑第一步放行,跑安全網,再放下一步。這個「AI 提案、人審核、小步驗證」的節奏,就是帶 AI 重構的正確姿勢——和 Day 11 除錯循環一樣,是超出效能測試的元技能。
六、資料夾結構與 README:給未來的自己留路標
整理的最後一步是讓結構自己說話。建議的樣子(依團隊慣例調整):
perf-testing/
├── lib/ # 共用模組:auth.js、config.js
├── data/ # 測試資料:pizza-data.json 等
├── smoke/ # 冒煙腳本
├── journeys/ # 旅程與情境腳本
├── shapes/ # 負載形狀腳本(load/spike/...)
└── README.md # 每支腳本的用途與執行指令
README 不用手寫:
Prompt 4|生成 README
請掃描整個資料夾,生成 README.md:
每支腳本一列,包含:檔名、一句話用途、執行指令範例、
需要的環境變數。用繁體中文,表格呈現。
之後我新增腳本時,會請你更新這份檔案。
三個月後回來、或同事接手時,第一眼看 README 就知道全貌——這份文件的價值,會在你最想不起來的那天兌現。
七、注意事項:整理的分寸

給 RD 的一句話:QA 的測試腳本開始有 lib/、有 README、有小步重構的紀律時,它就夠格進你們的版控庫了。主動邀請腳本進 repo、給一次善意的 code review,測試資產從此和產品程式碼同等待遇——這是效能測試在團隊裡扎根的標誌。
八、觀念驗證:三個問題確認你有帶走今天的重點
• 登入 API 多了一個必填欄位——整理前你要改幾個檔案?整理後呢?這說明哪條紀律的價值?(第一、四節)
• AI 的整理計畫建議「把所有腳本合併成一支超級腳本,用參數控制」——你會核准嗎?用哪條注意事項回應?(第五、七節)
• 為什麼「每改一小步就跑一次 smoke」不是浪費時間?它把最壞情況從什麼變成了什麼?(第四節)
九、小結
今天把兩週的腳本從「能跑」升級成「能維護」:三條紀律(單一事實來源、設定與邏輯分離、命名講人話)診治了三種病;環境變數讓腳本與環境解耦,共用模組讓登入只寫一次;README 給未來的自己留了路標。更重要的是那個工作方式——AI 提案、你審核、小步前進、每步用 smoke 作證。行為不變的重構有測試保護,這句話從今天起,你不只聽得懂,還做過了。
附錄:整理慣例速查
// 環境切換樣板
const BASE = __ENV.BASE_URL || 'https://quickpizza.grafana.com';
// 共用模組:匯出與引用
export function registerAndLogin(baseUrl) { /* ... */ } // lib/auth.js
import { registerAndLogin } from './lib/auth.js'; // 使用端
# 重構安全網
k6 run --vus 1 --duration 10s journey.js # 每一小步後跑一次
• 三條紀律:單一事實來源、設定與邏輯分離、命名講人話
• 三次再抽:重複第三次才抽模組;抽完要更好讀
• 節奏:AI 提案 → 你審核 → 小步改 → smoke 作證 → 下一步