戰(zhàn):自然語言驅(qū)動(dòng)UI自動(dòng)化測(cè)試)
上周我被測(cè)試群里一張截圖弄得有點(diǎn)沮喪。一條跑了三年的核心下單鏈路用例因?yàn)榍岸税训卿洶粹o的id從login-btn改成submit-login一夜間紅了 26 條。新同事說改選擇器就能解決但沒有人知道這個(gè)按鈕在第三個(gè)版本里還會(huì)不會(huì)再改一次。這種時(shí)刻我意識(shí)到傳統(tǒng) UI 自動(dòng)化最貴的成本不是寫用例而是維護(hù)用例。于是我開始認(rèn)真看 midscene 這個(gè)方向一個(gè)用自然語言驅(qū)動(dòng)瀏覽器、讓 AI 智能體自己看頁面、做操作、驗(yàn)結(jié)果的框架。這篇文章我會(huì)把 midscene 智能體做 AI 自動(dòng)化測(cè)試的環(huán)境搭建、Demo 演示完整展開也會(huì)把midscene 被 Playwright 調(diào)用的原理這一部分講透。我不打算只給命令還會(huì)解釋每一步為什么這么做、實(shí)測(cè)中會(huì)遇到哪些坑、怎么排錯(cuò)。適合正在評(píng)估 AI Agent 做 Web 自動(dòng)化測(cè)試的團(tuán)隊(duì)也適合想親手跑通第一個(gè) AI 測(cè)試 Demo 的個(gè)人開發(fā)者。不管你之前用的是 Selenium、Playwright 還是 Appium這篇文章的思路都能接上。1. 傳統(tǒng)UI自動(dòng)化的脆弱性與midscene智能體的解題思路1.1 一次前端改版引發(fā)的用例雪崩先還原一下開頭那個(gè)場景。老用例長這樣// 用了三年突然紅了 await page.click(#login-btn);前端把按鈕id改成>mkdir midscene-demo cd midscene-demo npm init -y npm i -D midscene/web playwright typescript tsx npx playwright install chromium每一步我都解釋一下為什么這么做npm init -y生成一個(gè)默認(rèn)的package.json給項(xiàng)目一個(gè)包管理入口。npm i -D midscene/web playwright typescript tsx一次裝齊四個(gè)依賴。midscene/web是智能體 SDKplaywright是瀏覽器自動(dòng)化框架typescript用來寫 TS 腳本tsx是一個(gè)可以直接運(yùn)行 TypeScript 文件的工具省去單獨(dú)的編譯步驟。npx playwright install chromium下載 Chromium 瀏覽器內(nèi)核。這一步特別容易被人跳過因?yàn)閯偛琶髅靼戳?playwright怎么跑不起來原因很簡單playwright 的 npm 包只是客戶端瀏覽器內(nèi)核需要單獨(dú)下載。如果下載超時(shí)先檢查本機(jī)網(wǎng)絡(luò)能否正常訪問外部網(wǎng)絡(luò)和 DNS 解析不要跳過這步后面所有腳本都依賴這個(gè)瀏覽器內(nèi)核。3.3 模型參數(shù)的環(huán)境變量配置先把模型的三個(gè)環(huán)境變量配好export AI_API_KEY你的模型服務(wù)密鑰 export AI_BASE_URL你的模型服務(wù)商兼容接口地址 export AI_MODELgpt-4o 或 qwen-vl 等同類型支持視覺的模型ID這里有一個(gè)很關(guān)鍵的實(shí)踐絕對(duì)不要把密鑰寫死在代碼里。一方面測(cè)試腳本經(jīng)常會(huì)提交到倉庫里密鑰一旦提交就等于泄露另一方面CI 環(huán)境里換模型只需要改環(huán)境變量不需要?jiǎng)哟a。團(tuán)隊(duì)內(nèi)部如果統(tǒng)一走模型網(wǎng)關(guān)也可以把AI_BASE_URL指向網(wǎng)關(guān)地址方便統(tǒng)一計(jì)費(fèi)和審計(jì)。配完之后可以用下面的命令驗(yàn)證一下環(huán)境變量是否已經(jīng)生效echo $AI_API_KEY如果輸出為空說明當(dāng)前終端會(huì)話沒有拿到變量檢查一下是否用了錯(cuò)誤的變量名。3.4 用最小腳本驗(yàn)證智能體真的能操作頁面環(huán)境裝好之后寫一個(gè)最小的驗(yàn)證腳本業(yè)務(wù)邏輯越簡單越好避免把問題混在一起。新建first-run.tsimport { chromium } from playwright; import { PlaywrightAgent } from midscene/web; async function main() { const browser await chromium.launch({ headless: false }); const page await browser.newPage(); const agent new PlaywrightAgent(page); await page.goto(https://www.saucedemo.com/); await agent.runTask(在用戶名輸入框填入 standard_user在密碼輸入框填入 secret_sauce); await agent.runTask(點(diǎn)擊 LOGIN 按鈕并斷言頁面出現(xiàn) Products 標(biāo)題); await browser.close(); } main();運(yùn)行npx tsx first-run.ts如果你裝到的 midscene 版本入口路徑不同比如midscene/web/playwright下導(dǎo)出以你裝到的版本提示為準(zhǔn)核心用法不變創(chuàng)建PlaywrightAgent傳入 Playwright 的page對(duì)象然后runTask下指令。跑起來之后你會(huì)看到瀏覽器窗口自己打開自動(dòng)填入賬號(hào)密碼點(diǎn)擊登錄然后進(jìn)入商品列表頁。這一步跑通說明你的環(huán)境基本沒問題。4. 可復(fù)現(xiàn)的Demo自然語言驅(qū)動(dòng)登錄與自動(dòng)驗(yàn)證4.1 目標(biāo)場景選型為什么選一個(gè)公開練習(xí)站選場景是有講究的。第一次跑 AI 智能體千萬不要拿公司內(nèi)部復(fù)雜的后臺(tái)系統(tǒng)練手因?yàn)槿魏我粋€(gè)環(huán)境變量、權(quán)限、彈窗問題都會(huì)讓你分不清是環(huán)境問題還是智能體問題。我推薦用 SauceDemo 這個(gè)公開練習(xí)站。它專門為自動(dòng)化測(cè)試設(shè)計(jì)登錄賬號(hào)固定、商品列表穩(wěn)定、購物車流程完整頁面結(jié)構(gòu)也不復(fù)雜非常適合驗(yàn)證AI 智能體到底能不能看懂頁面并執(zhí)行流程。場景設(shè)計(jì)如下打開 SauceDemo 首頁。用 standard_user 登錄。進(jìn)入商品詳情頁把 Sauce Labs Backpack 加入購物車。進(jìn)入購物車頁面斷言商品存在并取回價(jià)格。這個(gè)流程覆蓋了輸入、點(diǎn)擊、跳轉(zhuǎn)、斷言、數(shù)據(jù)提取幾個(gè)核心能力足夠說明問題。4.2 完整Demo腳本登錄、斷言、再到加購新建demo-flow.tsimport { chromium } from playwright; import { PlaywrightAgent } from midscene/web; async function main() { const browser await chromium.launch({ headless: false }); const page await browser.newPage(); const agent new PlaywrightAgent(page); await page.goto(https://www.saucedemo.com/); await agent.runTask(完成登錄使用 standard_user 登錄密碼 secret_sauce然后點(diǎn)擊登錄按鈕); await agent.runTask(在商品列表中點(diǎn)擊名稱為 Sauce Labs Backpack 的商品圖片進(jìn)入詳情頁); await agent.runTask(點(diǎn)擊 Add to cart 按鈕然后點(diǎn)擊購物車圖標(biāo)); await agent.runTask(斷言購物車頁面里商品名包含 Sauce Labs Backpack并輸出商品價(jià)格); await browser.close(); } main();這里四個(gè)runTask分別對(duì)應(yīng)四段意圖。你會(huì)發(fā)現(xiàn)整個(gè)腳本里沒有任何一個(gè) XPath 或 CSS 選擇器。所有定位工作全部交給智能體完成。4.3 運(yùn)行鏈路解讀規(guī)劃日志、執(zhí)行日志與截圖痕跡跑這個(gè)腳本的時(shí)候控制臺(tái)會(huì)輸出類似下面的信息模型當(dāng)前認(rèn)為頁面處于什么狀態(tài)、計(jì)劃執(zhí)行哪些動(dòng)作、動(dòng)作用了什么參數(shù)、執(zhí)行結(jié)果如何。建議你在跑的時(shí)候打開兩個(gè)終端一個(gè)跑腳本一個(gè)觀察運(yùn)行目錄。我實(shí)測(cè)中的輸出節(jié)奏大致是模型收到任務(wù)完成登錄先觀察頁面截圖。模型產(chǎn)生規(guī)劃第一步定位用戶名輸入框第二步定位密碼框第三步定位登錄按鈕。Playwright 依次執(zhí)行點(diǎn)擊、填入、再點(diǎn)擊。模型再次截圖確認(rèn)頁面已經(jīng)跳轉(zhuǎn)到商品列表頁任務(wù)完成。注意這個(gè) Demo 跑通后項(xiàng)目目錄里往往會(huì)生成報(bào)告或截圖文件。這些痕跡文件是調(diào)試的關(guān)鍵如果某一步出了問題你可以打開當(dāng)時(shí)的截圖看模型眼中的頁面和你眼中的頁面是否一致。很多時(shí)候你以為頁面長這樣其實(shí)彈窗、蒙層、加載態(tài)把關(guān)鍵元素?fù)踝×恕?.4 斷言失敗時(shí)智能體的自主容錯(cuò)行為傳統(tǒng)自動(dòng)化里斷言失敗就是用例失敗紅色一片。midscene 的機(jī)制不太一樣runTask里的斷言更像一個(gè)目標(biāo)校驗(yàn)步驟。如果第一次斷言失敗智能體不會(huì)直接放棄它會(huì)重新截圖、重新思考嘗試修正操作。舉個(gè)實(shí)際例子如果登錄按鈕被一個(gè)彈窗遮住了傳統(tǒng)腳本會(huì)直接超時(shí)報(bào)錯(cuò)。midscene 智能體則會(huì)先看到有個(gè)彈窗然后判斷這個(gè)彈窗是否需要關(guān)閉再執(zhí)行關(guān)閉動(dòng)作最后繼續(xù)登錄。我把這個(gè)特性叫做智能體自愈。它非常像真人測(cè)試員的工作方式第一次沒找到按鈕先看看是不是被什么擋住了處理完障礙再試一次。這也是它解決UI 自動(dòng)化脆弱性的核心價(jià)值——不是消除錯(cuò)誤而是在出錯(cuò)時(shí)有能力自己糾偏。當(dāng)然自愈不是萬能的。如果一個(gè)元素壓根不存在或者頁面直接白屏模型重試幾次仍然會(huì)失敗。所以后面要講重試策略和失敗現(xiàn)場保護(hù)。5. 接進(jìn)現(xiàn)有測(cè)試體系與pytest、Appium、接口自動(dòng)化共存5.1 不推翻pytest讓智能體管看得見的流程我見過不少團(tuán)隊(duì)聊 AI 自動(dòng)化測(cè)試第一反應(yīng)是是不是要把現(xiàn)有的 pytest 框架全換掉完全沒必要也不應(yīng)該。pytest 這類框架最擅長的東西測(cè)試編排、數(shù)據(jù)斷言、fixture 管理、報(bào)告輸出、CI 集成。這些能力 midscene 替代不了也不應(yīng)該替代。midscene 最擅長的東西是看得見的 UI 流程。所以合理的架構(gòu)是分層協(xié)作pytest 負(fù)責(zé)調(diào)度和斷言整個(gè)測(cè)試報(bào)告、執(zhí)行順序、數(shù)據(jù)準(zhǔn)備、接口校驗(yàn)仍然歸 pytest 管理。midscene 負(fù)責(zé)走流程把需要真實(shí)瀏覽器操作的步驟封裝成智能體腳本。最后的 UI 結(jié)果可以由 midscene 斷言也可以由 pytest 再做一輪兜底校驗(yàn)。這樣做的好處是團(tuán)隊(duì)不需要推翻現(xiàn)有的 CI 流水線和測(cè)試資產(chǎn)只是新增一種用 AI 智能體執(zhí)行 UI 流程的能力。5.2 pytest側(cè)封裝輕量入口的示例先寫一個(gè)可復(fù)用的 midscene 流程腳本比如scripts/midscene_flow.ts它能退出碼表示成功或失敗。然后 pytest 里用 subprocess 調(diào)用它import subprocess def test_saucedemo_login_flow(): result subprocess.run( [npx, tsx, scripts/midscene_flow.ts], capture_outputTrue, textTrue, ) assert result.returncode 0, result.stdout result.stderr這種方式最粗暴但也最穩(wěn)pytest 把 midscene 腳本當(dāng)黑盒只需要知道流程走沒走通。如果團(tuán)隊(duì)要求更精細(xì)的控制可以把 midscene 的斷言結(jié)果輸出成 JSONpytest 讀取 JSON 里返回的數(shù)據(jù)比如商品價(jià)格再做數(shù)值斷言。這里有個(gè)很重要的思路UI 流程交給 AI數(shù)據(jù)斷言回到傳統(tǒng)框架。AI 負(fù)責(zé)找到那個(gè)按鈕并點(diǎn)下去傳統(tǒng)斷言負(fù)責(zé)價(jià)格算得對(duì)不對(duì)。各干各最擅長的事整體穩(wěn)定性才會(huì)高。5.3 移動(dòng)端Appium場景先分清WebView與原生控件聊到 Appium這里要潑一點(diǎn)冷水midscene 這類視覺智能體在移動(dòng)端 WebView 或 H5 頁面可以用但原生控件的場景不算順暢。原因很簡單原生控件不在網(wǎng)頁 DOM 里Playwright 管不到智能體的視覺截圖與原生控件沒有直接的映射通道。如果你當(dāng)前團(tuán)隊(duì)以 Appium 做移動(dòng)端自動(dòng)化建議這樣考慮純?cè)撁胬^續(xù)用 Appium但可以把 AI 用在語義化斷言上。比如 Appium 取到頁面源碼和控件樹之后喂給大模型做意圖判斷這個(gè)頁面是否是登錄成功后的首頁。WebView / H5 頁面如果應(yīng)用的業(yè)務(wù)頁面對(duì)應(yīng)的是 H5可以考慮讓 midscene 接管瀏覽器上下文用視覺方案跑 WebView 內(nèi)的流程?;旌蠄鼍跋扔?Appium 切到正確的 WebView context再把頁面交給 Playwright 控制最后 midscene 接管操作。移動(dòng)端這塊建議先做小范圍驗(yàn)證不要一上來就把核心 Appium 資產(chǎn)遷移過來。等 WebView 場景跑穩(wěn)了再逐步擴(kuò)展。5.4 漸進(jìn)式遷移路徑與試點(diǎn)范圍從一個(gè)真實(shí)案例說起。我自己的團(tuán)隊(duì)做了一次小的漸進(jìn)式試點(diǎn)只用了兩個(gè)星期效果就出來了選一條最痛的鏈路從現(xiàn)有的幾百條用例里挑出一條每次發(fā)版必做、每次改版必紅的鏈路比如創(chuàng)建訂單-提交-付款-回顯。用 midscene 重寫這條鏈路把原來的 XPath 全刪掉改成自然語言任務(wù)。并行跑一周舊的用例繼續(xù)在 CI 里跑midscene 腳本也跑對(duì)比兩者的穩(wěn)定性、失敗率、維護(hù)時(shí)間。穩(wěn)定后替換新腳本連續(xù)一周穩(wěn)定后把舊腳本下線CI 里只留智能體版本。這個(gè)過程不要心急。一次遷移太多鏈路你會(huì)獲得一堆 AI 生成的失敗報(bào)告和燒掉的 token。從一條鏈路開始拿到信心和排錯(cuò)經(jīng)驗(yàn)后再擴(kuò)展。6. 實(shí)測(cè)中的坑、排錯(cuò)清單與一條判斷標(biāo)準(zhǔn)6.1 從運(yùn)行日志出發(fā)的完整排查鏈路智能體腳本報(bào)錯(cuò)之后第一反應(yīng)不要改代碼先看日志出現(xiàn)在哪一層。我按實(shí)際經(jīng)驗(yàn)整理了這樣一個(gè)排查順序環(huán)境變量層是否報(bào)401或missing api key 大概率是AI_API_KEY沒設(shè)置或設(shè)置錯(cuò)誤。模型調(diào)用層是否返回空結(jié)果、JSON 解析失敗 大概率是模型不支持視覺輸入或者請(qǐng)求內(nèi)容過大被服務(wù)商拒絕。規(guī)劃層模型有輸出但規(guī)劃的動(dòng)作明顯不對(duì) 比如點(diǎn)擊登錄按鈕規(guī)劃成了先點(diǎn)擊購物車。這種情況通常是模型對(duì)頁面理解有偏差換更強(qiáng)的多模態(tài)模型或把任務(wù)描述寫得更具體。執(zhí)行層規(guī)劃正確但 Playwright 報(bào)超時(shí)、元素不存在 看看頁面是否真的有彈窗、蒙層、懶加載把等待時(shí)間調(diào)大。驗(yàn)證層操作全部完成但智能體仍然認(rèn)為失敗 檢查斷言措辭是否太模糊比如檢查首頁正常就不如斷言頁面出現(xiàn) Products 標(biāo)題明確。下面這張排查表我建議貼到團(tuán)隊(duì)的 wiki 里現(xiàn)象可能原因最快解法401 / missing api key環(huán)境變量沒配檢查AI_API_KEY模型輸出為空 JSON模型不支持視覺換多模態(tài)模型規(guī)劃動(dòng)作明顯錯(cuò)誤任務(wù)描述太籠統(tǒng)把目標(biāo)拆成更明確的子任務(wù)Playwright 點(diǎn)擊超時(shí)頁面彈窗、懶加載調(diào)大超時(shí) 先處理遮擋元素?cái)嘌苑磸?fù)失敗期望文案寫錯(cuò)對(duì)照頁面實(shí)際文案修改任務(wù)描述6.2 實(shí)測(cè)中高頻遇到的五個(gè)坑這一節(jié)是我跑斷腿換來的經(jīng)驗(yàn)逐條說坑一非多模態(tài)模型硬上一跑就掛。第一次我圖省事接了一個(gè)只支持文本的模型結(jié)果runTask執(zhí)行到第一步就報(bào)錯(cuò)了。看日志才發(fā)現(xiàn)智能體需要看圖文本模型根本給不出有效規(guī)劃。換支持視覺的模型之后一切正常??佣laywright 瀏覽器內(nèi)核忘裝。裝完playwrightnpm 包直接跑腳本報(bào)錯(cuò)Executable doesnt exist。跑一下npx playwright install chromium就解決。這是新手最容易卡住的一關(guān)。坑三模型溫度配置過高每次操作結(jié)果都不一樣。大模型默認(rèn)的溫度參數(shù)可能偏高同一個(gè)任務(wù)每次規(guī)劃出來的步驟會(huì)漂移。做測(cè)試自動(dòng)化時(shí)一定要把溫度調(diào)低理想情況設(shè)為 0讓輸出盡可能穩(wěn)定??铀娜蝿?wù)描述太籠統(tǒng)。我試過直接說買一個(gè)背包模型在商品列表頁反復(fù)橫跳因?yàn)樗恢涝擖c(diǎn)哪個(gè)商品。改成點(diǎn)擊名稱為 Sauce Labs Backpack 的商品圖片進(jìn)入詳情頁之后一次就準(zhǔn)了。任務(wù)描述越具體智能體越穩(wěn)定??游迕つ孔非笠淮闻艽罅坑美?。智能體每個(gè)任務(wù)都要多次截圖和模型推理Token 消耗遠(yuǎn)高于普通接口調(diào)用。跑一百條 UI 用例的成本可能比跑一萬條接口用例還高。務(wù)必要把智能體用在刀刃上。6.3 成本控制token、超時(shí)與重試策略成本控制是 AI 測(cè)試能不能落地的關(guān)鍵。說幾個(gè)我摸索出來的硬指標(biāo)一次簡單操作的 Token 消耗一個(gè)登錄任務(wù)大概會(huì)經(jīng)歷 3-5 次截圖和模型調(diào)用每次調(diào)用消耗幾百到上千 Token。一個(gè)復(fù)雜流程跑下來可能消耗上萬 Token。這個(gè)量級(jí)在可控范圍但如果你拿它跑全量回歸賬單會(huì)非??捎^??刂浦卦嚧螖?shù)默認(rèn)情況下智能體失敗后會(huì)自動(dòng)重試。建議設(shè)置合理的最大重試次數(shù)比如 2-3 次。再多就是浪費(fèi)而且大概率是環(huán)境問題而不是智能體問題。把大任務(wù)拆小不要讓智能體從打開瀏覽器一路做到驗(yàn)證數(shù)據(jù)庫?;A(chǔ)導(dǎo)航和等待用 Playwright 直接寫代碼完成只在關(guān)鍵決策點(diǎn)啟用智能體。比如page.goto(url)用代碼直接導(dǎo)航登錄、選擇商品、驗(yàn)證結(jié)果這幾步交給智能體。這樣既省 Token又降低模型規(guī)劃出錯(cuò)的概率。失敗現(xiàn)場一定要留證據(jù)無論報(bào)告還是截圖都要落盤。因?yàn)?AI 智能體的失敗往往是概率性的沒有現(xiàn)場截圖你很難判斷是模型規(guī)劃錯(cuò)了還是頁面狀態(tài)不對(duì)。6.4 引入AI測(cè)試智能體的判斷標(biāo)準(zhǔn)與個(gè)人體會(huì)最后說說我的判斷標(biāo)準(zhǔn)這套標(biāo)準(zhǔn)現(xiàn)在是我們團(tuán)隊(duì)決定要不要用 AI 智能體的參考如果團(tuán)隊(duì)用例維護(hù)成本占測(cè)試開發(fā)時(shí)間超過一半值得試點(diǎn)。如果某條鏈路每次改版必紅優(yōu)先試點(diǎn)。如果頁面有大量動(dòng)態(tài)元素、頻繁改版的組件優(yōu)先試點(diǎn)。如果幾千條用例都是點(diǎn)幾下、填個(gè)框、看結(jié)果的重復(fù)流程需要謹(jǐn)慎評(píng)估成本。我在實(shí)際項(xiàng)目中把一條支付鏈路交給了 midscene。剛開始的幾天它每天都會(huì)紅一兩次多數(shù)原因是模型對(duì)某些彈窗的誤判和頁面時(shí)序問題。后來統(tǒng)一了模型、把溫度降到 0、給任務(wù)描述加上了明確的邊界條件穩(wěn)定度明顯上去。最明顯的變化是前端改版之后以前我要花半天改選擇器現(xiàn)在只需要看一遍智能體的報(bào)告確認(rèn)它通過視覺找到了新的元素位置然后把舊用例下線。當(dāng)然我也必須說實(shí)話AI 智能體不會(huì)解決所有測(cè)試問題。數(shù)據(jù)斷言、精確計(jì)算、穩(wěn)定性保障這些仍然要依靠傳統(tǒng)的測(cè)試框架和斷言手段。它解決的是定位元素、維護(hù)選擇器、應(yīng)對(duì)前端改版這一層最讓人頭疼的問題而不是整個(gè)測(cè)試體系。如果你正在考慮嘗試我的建議是不要從全量回歸開始選一條你最痛的核心鏈路花一個(gè)下午把環(huán)境搭起來跑通上面這個(gè) Demo然后認(rèn)真觀察一周。AI 測(cè)試這件事只有親手跑一輪你才能真切感受到把維護(hù)成本從寫選擇器轉(zhuǎn)移到人類表達(dá)意圖是什么體驗(yàn)。