證據驅動測試
記錄帶有註解的行為證明,然後將其附加到 PR 和追蹤問題。
錄影是你透過電腦操作測試應用程式的過程:啟動錄影器,然後親自操作應用程式 — 點擊、輸入、導航 — 逐一執行每個測試目標。影片中的每一個動作都是即時執行的測試;除非影片顯示了該互動過程,否則它作為證據毫無價值。如果工具鏈沒有電腦操作工具,但存在 GUI,請改用 cua-driver 來操作(見下文)— 這仍然是你的即時操作過程。
配套的錄影器 scripts/evidence.py 使用 FFmpeg 在 Linux、macOS 和 Windows 上擷取顯示畫面,為你在測試時添加的每個註解加上時間戳,在停止時將它們燒錄到影片中,並使用 ffprobe 驗證結果。它會在 session 資料夾中寫入 evidence.mp4、report.md 和 manifest.json。
輸入
- 測試目標(必填):要驗證的行為/流程,以可測試的陳述句表達。
- PR / issue(可選):張貼證據的位置。如果省略,則僅傳送給請求者。
錄影器
下面的 EVIDENCE 代表此技能資料夾內 scripts/evidence.py 的路徑(無論技能安裝在哪裡,例如 ~/.claude/skills/evidence-driven-testing/scripts/evidence.py)。它只需要 Python 3 和 FFmpeg。
-
先檢查:
python3 $EVIDENCE doctor— 驗證ffmpeg、ffprobe、libx264和ass濾鏡(ready),然後確認哪個螢幕擷取來源在此機器上可用(capture_ready並且來源auto會自動選擇)。僅在工具鏈缺失時才以非零值退出;在錄製之前請先閱讀capture_ready。 -
平台(
--source auto選擇第一個可用的):作業系統 來源 需求 Linux X11 / XWayland x11(x11grab)已設定 DISPLAYLinux Wayland wayland(wf-recorder)已設定 WAYLAND_DISPLAY,wf-recorder在 PATH 中,並且已確認合成器支援 wlr-screencopy — 可以是已知的 wlroots 合成器(Sway、Hyprland、river、Wayfire、labwc、dwl、niri)或透過wayland-info驗證。GNOME 和 KDE Wayland 無法以此方式擷取;doctor會告知。對於 X11 應用程式請透過 XWayland 使用x11,或使用備用錄影器。--source wayland仍會強制使用它macOS avfoundation已授予終端機 / 代理主機應用程式螢幕錄製權限; doctor會列出--screen-index可用的螢幕索引Windows gdigrab任何標準的 ffmpeg 建置;使用 powershell進行行程檢查預設為全螢幕擷取;
--geometry WxH和--offset X,Y可在 x11、wayland 和 gdigrab 上裁剪區域。XWayland 只能看到 X11 視窗,因此當被測應用程式是 Wayland 原生時,請優先使用wayland來源。 -
崩潰安全原始錄製:原始錄製是 MPEG-TS(
raw.ts),因此如果錄影器被強制終止或崩潰,已擷取的內容仍然可以探查和渲染。stop會將其重新混合或重新編碼為標準的evidence.mp4。 -
停止方式:在 Linux 上,透過 pidfd 向錄影器發送訊號。在 macOS 和 Windows 上,
start會啟動一個小型監督行程,該行程擁有 ffmpeg 子行程,而stop則要求它(透過 session 資料夾中的stop.request)中斷、終止,然後殺死;完成後會寫入recorder-exit.json。永遠不會向裸 PID 發送訊號,因此不會影響到被回收的 PID。如果監督行程在錄影器仍在運行時死亡,stop會拒絕並告訴你需要手動停止哪個 PID。如果它在記錄已啟動的行程之前就死亡,stop會保持阻塞狀態,直到你親自檢查是否有殘留的錄影器,然後使用--accept-untracked-recorder重新運行它;報告隨後會包含該注意事項。 -
備用錄影器:當
doctor報告沒有擷取來源時(例如沒有 wf-recorder 的 Wayland):cua-driver recording start <dir>/stop(請參閱 cua-driver 部分),或使用作業系統的錄影器(macOS:screencapture -v out.mov)。在這些路徑上沒有註解疊加層,因此請將註解協議保留為檔案 — 一個assertions.md,列出每個setup/test_start/assertion及其結果和近似影片時間戳,與 headless 路徑完全相同。 -
永遠不要將
--source test(合成圖案產生器)作為 UI 證據呈現。它僅用於對工具鏈進行冒煙測試;儲存庫中的tests/test_evidence.py會使用它。
操作說明
1. 準備螢幕
- 最大化瀏覽器/應用程式視窗;關閉彈出視窗、通知和額外面板。
- 在錄製之前導航到起始狀態(已登入、正確的頁面),除非設定本身是待測項目。
- 記錄正在測試的確切修訂版本:
git rev-parse HEAD和git branch --show-current(或部署 URL)— 錄影器會將它們蓋印到報告中。
2. 開始錄製
-
在第一個有意義的動作之前開始螢幕錄製:
python3 $EVIDENCE start \ --output .artifacts/<task-name> \ --title "<正在驗證的內容>" \ --commit "$(git rev-parse HEAD)" --branch "$(git branch --show-current)" \ --environment "<作業系統 / 瀏覽器 / 顯示 / 部署>"它會列印一個包含
session路徑和所選source的 JSON;請保留該路徑(SESSION=...)以供後續所有命令使用。來源會自動偵測並擷取整個螢幕;僅在doctor或情況要求時才傳遞--source、--geometry、--offset、--display/--xauthority(X11)、--screen-index(macOS) 或--output-name(Wayland)。 -
添加一個描述起始上下文的
setup註解:python3 $EVIDENCE annotate "$SESSION" --type setup \ --message "已登入,正在導航到連接器頁面"
3. 透過電腦操作進行測試,邊測試邊註解
-
透過在即時應用程式上進行電腦操作來執行每個互動 — 錄影會捕捉你的操作過程,因此測試和證據是同一行為。以可觀看的速度操作:在每個動作後讓 UI 穩定下來,以便狀態變更在影片中可見。
-
在每個命名的測試開始時,以 Jest 風格添加一個
test_start註解:python3 $EVIDENCE annotate "$SESSION" --type test_start \ --message "當權限為 'always' 時,它應直接執行工具" -
在每次檢查之後,添加一個帶有
--result passed、failed或untested的assertion註解:python3 $EVIDENCE annotate "$SESSION" --type assertion --result passed \ --message "工具在沒有權限提示的情況下運行" -
斷言規則:
- 每個有意義的狀態變更對應一個斷言 — 合併,不要為每個 UI 標籤都添加註解。
- 使用 "Precondition: ..." 斷言來建立起始狀態。
- 保持在 80 個字元以內,高信號(錄影器會拒絕更長的消息)。
- 如果測試無法運行(缺少先決條件、授權視窗已過期),將其標記為
untested並說明原因 — 永遠不要跳過而不記錄。 - 時間戳記錄的是你斷言的時間,而不是它是否為真 — 在選擇
passed之前請先查看螢幕。
4. 停止並審查
-
在最後一個斷言之後停止錄製:
python3 $EVIDENCE stop "$SESSION"這會停止擷取(優雅地,以便錄影器刷新;只有在忽略請求時才會升級),將註解燒錄到
evidence.mp4中,探查結果,並在旁邊寫入report.md和manifest.json。成功時會列印"verified": true;如果渲染失敗,session 會被標記為finalization_failed— 修正報告的原因並再次運行stop(重試不會向錄影器發送兩次訊號)。如果無法再安全地向錄影行程發送訊號(它已死亡,或其 PID 現在屬於另一個行程),session 會被標記為recorder_lost。再次運行stop會完成任何已擷取的影片,但僅在確認該錄影行程已消失後 — 如果它仍在運行,請先停止它,否則影片可能會在仍在寫入時被渲染。 -
在分享之前確認錄影捕捉到了關鍵時刻:在每個斷言時間戳處提取一個影格(
ffmpeg -ss <t> -i evidence.mp4 -frames:v 1 frame.png)並檢查狀態和標籤是否可見。 -
填寫
report.md的「注意事項」(Caveats) 部分;永遠不要留下佔位符。
5. 張貼證據
report.md就是報告:測試了什麼、環境 + 確切提交、每個測試的通過/失敗、注意事項。請對其進行擴展,而不是從頭重寫。- 將影片 + 摘要作為 PR 評論張貼(如果是你的 PR,則嵌入到 PR 描述中)。
gh pr comment無法附加本地影片 — 請在已認證的瀏覽器中透過 PR 的評論框上傳evidence.mp4,或將其上傳到主機並連結(例如before-and-after上傳適配器)。重新開啟評論並確認影片可以播放,然後再聲稱已張貼。 - 將相同的影片附加到追蹤問題(Linear/Jira),並附上一行結果。
- 將報告 + 錄影發送給請求者。
防護措施
- 影片必須顯示實際的測試操作過程。切勿將腳本播放、拼接剪輯或合成素材作為錄影呈現;如果工具鏈缺乏電腦操作工具但存在 GUI,請透過
cua-driver操作;完全沒有 GUI 時,請改用 headless 路徑。 - 切勿錄製半遮蓋或並排的視窗 — 請先最大化。
- 切勿錄製顯示機密、令牌、客戶資料或付款詳細資訊的螢幕;如果某個流程需要它們,請將其標記為
untested並說明原因。 - 在驗證修復時,請連同新的成功一起顯示或參考舊的失敗。
- 始終說明測試時的精確提交/分支/部署。
沒有電腦操作工具?使用 cua-driver 操作(有 GUI)
當存在顯示器但代理沒有內建的電腦操作能力時,請使用 cua-driver(macOS / Windows / Linux)作為執行器。這仍然是你即時測試應用程式 — 錄製規則保持不變;僅輸入機制不同。
- 在錄製之前使用
cua-driver doctor驗證設定。如果安裝了cua-driver技能,請閱讀它並遵循其協議 — 操作前快照(snapshot-before-action)的不變性是強制性的。 - 每個互動輪迴:
launch_app→get_window_state(無障礙樹 + 螢幕截圖)→ 透過element_token操作(click、type_text、press_key)→verify_state驗證預期的後置條件。每個verify_state檢查都對應一個assertion註解。 - 只要
doctor報告capture_ready: yes,就繼續使用上述配套的錄影器進行影片和註解;cua-driver 僅提供輸入。 - 否則,
cua-driver recording start <output-dir>/cua-driver recording stop就是錄影器(需要輸出目錄,且守護行程必須正在運行:cua-driver serve)。影片擷取預設為啟用,並在停止時完成到<output-dir>/recording.mp4— 但在 Windows/Linux 上,它會 shell 呼叫 ffmpeg,因此缺少 ffmpeg 或顯示器只會產生每輪的軌跡資料夾(前/後螢幕截圖、action.json、click.png),不會有影片。停止後,在引用recording.mp4之前,請先確認它是否存在;如果不存在,請修復錄影器,或根據 headless 協議將每輪的前/後螢幕截圖呈現為編號的擷取。 - 如果在此路徑上沒有可用的註解疊加層,請將協議保留為檔案:一個
assertions.md,列出每個test_start/assertion及其結果,與 headless 路徑完全相同。
Headless 路徑(無 GUI)
當代理沒有桌面可錄製時,保持相同的斷言紀律;將錄影器替換為腳本式擷取:
-
將所有內容儲存到
.artifacts/<task-name>/(將其加入.gitignore— 證據會被上傳,永遠不會提交)。將擷取腳本與擷取的內容放在一起,以便運行是可重現的。 -
螢幕截圖:
before-and-afterCLI(@vercel/before-and-after)會擷取 URL 或元素,其配對可以直接饋送給 PR 嵌入。在容器/虛擬機器中,如果 Chrome 失敗並顯示 "No usable sandbox",請設定AGENT_BROWSER_ARGS="--no-sandbox"。 -
影片 / 多步驟流程:一個一次性的 Playwright 腳本,無需將 playwright 添加到專案的依賴項即可運行:
npx --yes --package=playwright node record.mjs(單純使用
npx playwright node record.mjs會失敗 —node不是 Playwright CLI 命令;--package=playwright才是將模組放到路徑上的方法。)最小化的record.mjs:import { chromium } from "playwright"; const browser = await chromium.launch(); const context = await browser.newContext({ recordVideo: { dir: ".artifacts/<task-name>/" }, }); const page = await context.newPage(); await page.goto("http://localhost:3000/path-under-test"); // ...驅動流程,每個步驟一個有意義的狀態變更... await context.close(); // 完成 .webm await browser.close();如果檔案很大,可以使用 ffmpeg 修剪或壓縮。
-
註解協議變成檔案:按測試順序對擷取內容進行編號,並在名稱中包含斷言 —
01-precondition-signed-in.png、02-it-saves-on-blur-passed.png— 並在 artifacts 資料夾中保留一個assertions.md,列出每個test_start/assertion及其結果(passed/failed/untested+ 原因)。
非 UI 變更仍需要證據
- API / 效能:一個帶有測量數字的腳本探查 — 每個階段的請求計數、前/後延遲 — 擷取到
probe-output.txt。 - 渲染 / Canvas / 著色器:渲染的影格加上像素斷言(差異值),由人工檢查並儲存為 PNG。
- 代理行為:顯示工具呼叫和回應的相關對話記錄摘錄。
- 錯誤修復:在撰寫修復程式碼之前重現並擷取失敗 — 該擷取是前/後配對中的「前」半部分。
擷取衛生
- 確認你正在探查的伺服器正在運行你的程式碼(正確的埠、正確的行程),特別是在多個代理共享一台機器時:
lsof -i :<port>— 或者如果未安裝lsof,請使用ss -ltnp "sport = :<port>"。 - 在錄製之前,關閉可能改變應用程式狀態的其他工具(如除錯工具)。
- 如果一個測試步驟需要等待非同步操作(例如 API 呼叫),請在繼續之前確認操作已完成(透過 UI 狀態或網路日誌),而不是猜測時間。
- 對於 headless 擷取,確認檔案已完全寫入磁碟後再將其上傳或引用爲完成。
常見問題
| 問題 | 解決方案 |
|---|---|
doctor 說 ffmpeg 缺失 | 在 macOS 上透過 brew install ffmpeg、在 Ubuntu/Debian 上透過 sudo apt install ffmpeg、在 Windows 上透過 choco install ffmpeg 安裝,或從 ffmpeg.org 下載。確保它在 PATH 中。 |
doctor 說 capture_ready: no | 檢查 --source 參數是否與你的顯示伺服器相符(Linux 上的 X11 與 Wayland)。在 macOS 上,授予終端機螢幕錄製權限(系統設定 → 隱私與安全性 → 螢幕錄製)。在 Wayland 上,安裝 wf-recorder。 |
stop 報告 finalization_failed | 檢查 raw.ts 檔案;如果為空,可能是錄影器從未啟動。如果非空,則可能是 ffprobe 失敗 — 手動運行 ffprobe raw.ts 以查看錯誤。 |
| 註解未燒錄 | 確認你使用了正確的 SESSION 路徑,並且 annotate 命令在 stop 之前運行。檢查 manifest.json 中的註解計數。 |
gh pr comment 說影片太大 | 使用 ffmpeg -i evidence.mp4 -vf "scale=iw/2:ih/2" compressed.mp4 壓縮,或上傳到外部主機並連結。 |
| Playwright 無法啟動 Chrome/chromium | 設定 AGENT_BROWSER_ARGS="--no-sandbox" 並檢查 npx playwright install chromium。 |