山姆鍋在開發 NT² Vault ⎘ 時,為了少當那個發版前點系統匣的人,做成「截圖 → 本機視覺 → 模擬滑鼠鍵盤」的 Visual-QA——一件很像工程師白日夢的事:能不能用「極致模擬人眼+人手」,把自己從重複點擊裡解放出來。
先把動機講清楚:不只請不起 QA,也不想自己當 QA。 一人產品養不起專職測試;發版前還要親手點系統匣、原生選單、檔案關聯對話框,山姆鍋做得到,只是不想把人生耗在那裡。答案目前是:工具能跑一截,還半成品,但路上學到的技術選型,比「自動化打勾」有意思多了。
目錄
展開目錄
懶惰的起點:工程師不該是自己的點擊猴子
端到端(E2E)用 Playwright 之後,WebView 裡多數流程其實還算聽話:有 data-testid、有穩定等待,CI 也能扛。
真正會讓山姆鍋半夜起來罵的,是桌面殼那些沒有 selector 的東西——系統匣圖示、原生選單、關掉視窗後藏到 tray、檔案關聯跳出的系統對話框。人一眼就懂;自動化卻像隔著毛玻璃。
一人開發時,這類表面常常只剩自己能測。選項很窄:
- 發版前自己當 QA、當點擊猴子(會忘、會煩、會漏,也最討厭)。
- 當作「測不到就算了」(產品臉丟在使用者那裡)。
- 做一套看起來像人在測的東西,希望之後少親手點。
山姆鍋選了 3。說穿了就是懶:想一次投資工具,換掉以後無數次「自己上場點」。至於「為了解決問題順便學一堆新東西」——那本來就是山姆鍋的壞習慣,也是這次願意把洞挖這麼深的原因。
第一代白日夢:Browser-QA
第一個嘗試很「AI 時代」:本機起一個維運用的 Browser-QA 控制台,Playwright 開真瀏覽器,旁邊掛本機 LM Studio,讓模型用工具迴圈去點、填、截圖、判定。感覺像請到一位永不抱怨的實習生——剛好滿足「別叫我自己點」的幻想。
現實很快打臉:
- 跟既有 E2E 搶地盤。 很多其實有 selector 的流程,被重做成「模型在瀏覽器裡瞎逛」,維護兩套「像人」的自動化,比維護一套確定性測試還累。
- 夠不著 OS chrome。 Playwright 再強,也點不到系統匣與原生選單那一層;而山姆鍋最痛的剛好在那裡。
- Agent 迴圈很性感,也很難當契約。 自由發揮的規劃器適合探索,不適合當「發版前可重複的腳本」。
後來這條路正式退役:能掛在 selector/等待上的,歸回 Playwright;必須看螢幕才能動的,另開一層;硬體、生物辨識、以及「確定沒有跳出某提示」這類負向證據,繼續留給——唉——偶爾還是得自己上場的人。中間那層,就變成後來的 Visual-QA。
夢想長什麼樣:人怎麼測,機器就怎麼測
重新想清楚時,山姆鍋給自己設了一條幾乎偏執的 I/O 契約:
| 通道 | 只准這樣 | 故意不准 |
|---|---|---|
| 觀察/決定下一步 | 螢幕截圖的像素,交給本機視覺解讀 | 無障礙樹、AppleScript 點選單、DOM/Playwright、雲端視覺 API、憑空寫死座標 |
| 操作桌面 | 模擬滑鼠與鍵盤 | 用無障礙 API 的 performAction、用腳本依名稱選選單、靠視窗管理 API 假裝「點過了」 |
聽起來很矯情——明明 macOS 給了 Accessibility,為什麼不用捷徑?
因為山姆鍋想逼近的是人在當 QA 時的感官與致動器,不是「怎樣最快把測試變綠」。人看螢幕、移動游標、敲鍵盤;模擬器或遠端桌面在螢幕上也只是一塊視窗。契約綁死之後,同一個心智模型才能遷到「本機模擬器當視窗」這類場景,而不必讓工具學會 ADB/RDP/一堆遙控協定。
還有一層更實際的懶人邏輯:不要買訂閱制的 Node 桌面自動化套件(例如 nut.js 那類)。一人產品的固定成本神經很敏感;能用開源堆起來,就自己堆。
於是山姆鍋自己做了一套可攜的本機套件:Python、uv ⎘ 管理環境、OpenCV 做影像比對、mss 截圖、pynput 注入滑鼠鍵盤。跑測的那台機不必裝整包產品相依,解壓、授權螢幕錄製與輔助使用、一條指令跑 YAML playbook 就好。它刻意不是 CI 合併閘——不穩的視覺測試塞進 PR 紅燈,只會逼人關掉測試,而不是修好產品。
金字塔怎麼切:E2E ∪ Visual-QA ∪ Manual
懶人版分工大概是:
- E2E(Playwright/CI):有穩定 selector 或可程式等待的,就別跟像素玩。應用內 UI 優先加
data-testid,不要為了耍酷寫視覺腳本。 - Visual-QA(本機套件):只有「必須從螢幕像素找出來才能點或斷言」才進來——典型是 OS chrome。
- Manual:指紋、硬體、以及「確定沒出現」這種負向證據。
一句話:Visual-QA 是「請不起人、也不想自己當」時,對人眼必須上場那一小撮的加速器;不是第二套產品測試系統。
技術選型其實是一層層變懶
一開始山姆鍋以為 OpenCV 模板比對就夠:裁一塊 PNG,螢幕上看見就點中心。macOS 上先做出 tray 隱藏/還原這類 playbook 之後,痛苦立刻換皮——解析度、縮放、主題一變,模板就碎。於是有了 platform profile × scale 的目錄約定:同一個邏輯目標,磁碟上放多套圖,執行時明確指定 profile/scale,不靠「安靜猜你是哪台機」。
再懶一點:有些東西其實是畫面上的字。與其裁圖,不如本機 OCR(EasyOCR)做 wait_for_text/click_text/assert_text。仍不准走雲端 OCR——測試畫面再怎麼是本機建置,也不想預設把截圖送出箱子。
再再懶一點:模板還是要人裁,OCR 又吃字型與語言。於是把本機 視覺語言模型(VLM) 接進來:playbook 寫一句 find: "menu-bar tray icon for …",模型回結構化座標;斷言則用 expect:,強制回 {"ok": true|false} 這類 JSON,不准用散文蒙混過關。預設對準本機 LM Studio ⎘ 的 OpenAI-compatible 端點與 qwen/qwen3-vl-8b——URL 必須是 loopback,雲端視覺一樣拒絕。
這裡有個山姆鍋後來覺得還算清醒的分界:
intent:給人或寫 playbook 的 Agent 看的說明,不當 VLM prompt,也不當執行期規劃器。find:/expect::唯一送進模型的使用者字串;腳本本身仍是 YAML 順序的確定性劇本。
換句話說:模型是截圖解讀器,不是「自己決定測什麼」的 Agent。Browser-QA 那條自由迴圈,山姆鍋已經繳過學費。
最後一層懶人彩蛋:VLM locate 自動模板快取。第一次靠模型找到目標,就把裁切存進本機快取(不進版控);之後同一組 playbook/步驟/profile/scale/find: 雜湊,先用 OpenCV 碰運氣,沒命中再回 VLM 並覆寫快取。語意權威仍是 find:+模型;斷言永不走這份快取——山姆鍋可以接受「找按鈕」被加速,不能接受「看起來還好」被快取成假綠燈。
這整條演化,說穿了就是:先承認「人當 QA」時的介面是螢幕與鍵鼠,再一層層把「最煩、最重複、最不想親手做」的成本往本機視覺推,同時把「會騙人的捷徑」全部釘死。
一勞永逸?目前頂多是「一勞永逸的幻想」
誠實講,這套現在仍是半成品:
- 主力驗證還偏 macOS;Windows/Linux/行動模擬器比較像契約與目錄先就位,playbook 密度跟不上幻想。
- 本機 VLM 要顯存、要啟動、要等;冷啟動一跑,你會重新想念純 OpenCV。
- 快取會過期、主題會變、模型會看錯;失敗時你仍得當那個看 report 的人——離「完全不當 QA」還有一段。
- 它從來不該取代 E2E;該用 selector 自動化的,還是得回到確定性測試,不能靠「有視覺腳本」自我安慰。
所以「一勞永逸」比較像工程師安慰自己的口號。真正留下來的,是一組可攜、可講清楚邊界的工具,以及山姆鍋又一次確認:自己很愛為了解決問題去學習新技術,這次是 OpenCV、OCR、本機 VLM、playbook schema——有時學得比產品進度還開心,這點就讓讀者笑一下吧。
給同樣一人扛產品的人
沒有標準答案,只有山姆鍋願意再走一次的取捨:
- 先問這步是不是 E2E 該做的。 有 selector 就別玩像素。
- 若目標是模擬人當 QA,就把感官與致動器寫死。 捷徑愈多,愈不像「人測過」,跨表面也愈難搬。
- 視覺測試別當 PR 合併閘。 不穩的紅燈會教會團隊關閉測試,而不是修產品。
- 模型當解讀器,不當規劃器。 劇本保持確定性;
find:/expect:與說明用的intent分開。 - 快取加速 locate,斷言保持新鮮。 懶要懶在重複勞動,不要懶在信心。
- 接受半成品。 請不起人、又不想自己當 QA 時,目標是降低發版前親手點擊的次數與遺漏率,不是打造完美視覺 Agent。
這篇刻意不寫成「如何安裝 Visual-QA」逐步教學——工具 README 會過期,決策過程比較耐放。山姆鍋比較想留下的是:為什麼放棄 Browser-QA、為什麼堅持截圖+鍵鼠、以及為什麼本機 VLM 最後長成「加速器」而不是「新系統」。
若你對產品本身有興趣,官網在 nt2.me ⎘。一人開發系列裡,CI 分鐘數那篇寫過自架 runner——那又是另一種「懶,但得先投資」的故事。