
這期開源項目分享聊一個很戳我痛點的工具e2e。它解決的是我從接觸AI輔助測試開始就惦記的問題——E2E測試能不能用自然語言寫寫完之后又不至于每次跑回歸都燒大模型的調(diào)用費用。這個項目給出的答案是先用自然語言描述意圖讓Agent把操作跑通然后把跑通過的操作序列錄成一份“回放緩存”之后同樣的場景直接讀緩存執(zhí)行模型只在緩存沒命中的時候才出場。簡單說就是讓AI幫你探路讓腳本替你重復(fù)。適合誰看被E2E腳本維護折磨過的測試開發(fā)剛接觸AI編程想找落地場景的前端同學(xué)以及所有對“AI測試”感興趣但擔(dān)心成本和不確定性的人。下面我把這個項目的核心思路、實現(xiàn)機制、完整落地步驟和排障經(jīng)驗一次講透。1. 項目整體思路拆解為什么E2E測試需要“自然語言回放緩存”1.1 傳統(tǒng)E2E腳本的三大痛點傳統(tǒng)E2E測試腳本說白了就是把用戶操作翻譯成代碼定位元素、執(zhí)行動作、寫斷言。寫一次不難難的是后面的維護。痛點一選擇器脆弱。頁面改個class、調(diào)一下DOM層級、按鈕文案從“提交”改成“確認提交”腳本就可能崩。你花半天排查最后發(fā)現(xiàn)只是前端同事把CSS類名換了個寫法。痛點二流程編碼僵化。每個用例都是一段線性代碼登錄流程、下單流程、創(chuàng)建流程各寫各的遇到稍復(fù)雜的分支、異步加載、彈窗代碼里就塞滿sleep和輪詢惡心且難讀。痛點三頁面改版連帶故障。E2E腳本和頁面結(jié)構(gòu)深度耦合前端每次重構(gòu)測試腳本就得跟著返工。返工不是改一行是一整條鏈路重新定位元素、重新調(diào)試斷言。PageObject、數(shù)據(jù)驅(qū)動這些模式能在一定程度上緩解問題但解決不了本質(zhì)你仍然需要靠人去讀頁面、寫選擇器、維護映射關(guān)系。智能化程度高一點的錄制回放工具有了很大進步但生成出來的腳本一旦頁面變化照樣一片紅。1.2 LLM入場后的轉(zhuǎn)機與新瓶頸大模型出現(xiàn)后自然語言直接驅(qū)動瀏覽器操作成了熱點。你用一句話描述“打開登錄頁輸入賬號密碼點登錄驗證跳轉(zhuǎn)成功”模型就能把這句話拆成一系列可執(zhí)行動作并完成驗證。這帶來的體驗是革命性的測試思路從“寫代碼”變成了“說人話”。對業(yè)務(wù)理解深但對代碼不熟的人也能寫用例這是個很大的門檻降低。但如果每個用例每次都走模型實時驅(qū)動問題同樣接踵而來。成本問題最現(xiàn)實。一次普通頁面的操作可能需要幾千到上萬token。一個回歸測試包含幾十個用例每個用例跑一遍費用很快就上去了。CI里每天跑幾輪一個月下來賬單非??捎^。延遲問題也不容忽視。模型推理速度再快也要一兩秒甚至更久幾十步操作串行下來單用例跑個一分鐘很正常整體執(zhí)行時間被拉長。更大的問題是確定性。同一句話模型兩次生成的步驟可能不完全一樣元素定位邏輯也可能漂移。E2E測試最講究“可重復(fù)”如果測一次一個樣這種不確定性會讓測試結(jié)果很難讓人信任。1.3 “回放緩存”這個設(shè)計的巧妙之處e2e這個項目把自然語言驅(qū)動的高靈活性和傳統(tǒng)腳本的高確定性拼在了一起中間加了一層緩存。完整流程是這樣的第一次跑某個場景時模型負責(zé)把自然語言轉(zhuǎn)換成操作計劃Agent逐一步執(zhí)行并校驗結(jié)果。執(zhí)行成功后系統(tǒng)把這套已經(jīng)驗證過的操作序列錄成回放緩存。之后再次跑同一個場景系統(tǒng)直接讀緩存按照錄制好的步驟快速重放模型完全不參與。一旦頁面結(jié)構(gòu)變化導(dǎo)致緩存步驟失敗系統(tǒng)才重新喚醒模型規(guī)劃新路徑再把新路徑回寫進緩存。拿生活中的例子類比你第一次去陌生城市出差要開導(dǎo)航邊走邊看路牌最后找到酒店。第二次再去同一個城市你直接憑記憶走熟悉的路不用再打開導(dǎo)航。發(fā)現(xiàn)路被挖斷或封路了才重新掏出導(dǎo)航。這套設(shè)計把幾個核心問題一次解決運行成本降下來因為絕大多數(shù)步驟走緩存并不調(diào)模型執(zhí)行速度和傳統(tǒng)腳本一樣快甚至更快因為省去了模型的推理時間結(jié)果確定性也有了回放走的是固定序列不依賴模型情緒的波動頁面小變化也有了容錯因為緩存失敗會自動降級回LLM探索而不是直接報錯。用一張表看得更清楚維度傳統(tǒng)腳本/PageObject純LLM每次實時驅(qū)動自然語言回放緩存用例編寫方式寫代碼需要掌握框架API自然語言描述自然語言描述每次運行成本只有算力幾乎為零高token實時消耗極低命中緩存時不消耗執(zhí)行耗時快秒級慢受模型推理速度限制快與腳本相當(dāng)結(jié)果確定性確定性高但脆弱不確定性明顯確定性高回放固定序列應(yīng)對頁面小改版選擇器一改就掛能自適應(yīng)但每次都要模型出場緩存失效時自動降級模型重新規(guī)劃長期維護成本高靠人力逐條修中靠模型兜底但成本高低只在頁面邏輯變化時重錄2. 核心機制解析從自然語言到可執(zhí)行步驟再到緩存命中2.1 自然語言用例描述的正確姿勢e2e的用例文件本質(zhì)上是一份帶操作指令和斷言的Markdown文檔。它不需要你用任何框架語法但也不是隨便寫一句人話就行。我梳理下來比較推薦的結(jié)構(gòu)是動作 目標(biāo)元素 補充條件三段式。目標(biāo)是哪個頁面、哪個按鈕、哪個輸入框動作是什么有沒有額外條件都寫清楚。比如打開頁面 https://demo.xxxxxx.com/login 輸入框“用戶名”輸入 test_user 輸入框“密碼”輸入 test_pass 點擊按鈕“登錄” 等待出現(xiàn)“歡迎回來”“點擊登錄”“輸入賬號”這種極簡描述其實也能跑但遇到頁面上有多個相似文案的元素時模型就得靠猜。稍微寫具體一點比如“頁面右上角的登錄按鈕”“表單里的用戶名輸入框”命中率和穩(wěn)定性都會有明顯提升。斷言也要用自然語言描述但要注意描述的是“可觀察的狀態(tài)”不是“抽象的業(yè)務(wù)邏輯”。比如斷言“訂單狀態(tài)變?yōu)橐阎Ц丁本筒蝗纭绊撁娉霈F(xiàn)‘支付成功’提示且按鈕變?yōu)椤榭从唵巍敝庇^。Agent是靠當(dāng)前頁面的DOM、文本、可見性來判斷斷言是否成立的描述越貼近實際頁面表現(xiàn)校驗越準(zhǔn)。2.2 Agent的執(zhí)行鏈路感知、規(guī)劃、操作、校驗這一步是整個項目的技術(shù)核心。模型能不能把自然語言準(zhǔn)確映射到真實頁面決定了緩存錄出來的東西可不可信。執(zhí)行鏈路大致是感知、規(guī)劃、操作、校驗四步循環(huán)。感知階段Agent先抓取當(dāng)前頁面的DOM結(jié)構(gòu)、可訪問性樹、可見文本和關(guān)鍵控件信息形成對頁面狀態(tài)的完整“觀察”。這一步很關(guān)鍵如果頁面是用大量前端框架渲染出來的復(fù)雜應(yīng)用DOM樹會很龐大直接全量塞給模型會浪費大量token且降低響應(yīng)速度所以通常會做一輪精簡只保留按鈕、輸入框、鏈接、表單等可交互元素。規(guī)劃階段模型結(jié)合用戶寫的自然語言和當(dāng)前頁面狀態(tài)拆出下一個動作??赡苁屈c擊、輸入、滾動、等待、切換iframe也可能是斷言校驗。這里模型輸出的是結(jié)構(gòu)化指令不直接去操作瀏覽器而是交回給執(zhí)行層處理。操作階段執(zhí)行層按照結(jié)構(gòu)化指令調(diào)用瀏覽器自動化接口真實操作頁面。如果目標(biāo)元素有多個匹配會按置信度選擇最優(yōu)的拿不準(zhǔn)時寧可多問一次模型把條件細化也不要盲目點擊。校驗階段Agent檢查操作是否真正生效點擊后頁面有沒有跳轉(zhuǎn)輸入后值有沒有回顯按鈕是否從禁用變?yōu)榭捎?。校驗不通過就觸發(fā)糾偏邏輯比如重新定位元素、換一種方式操作、或者繼續(xù)觀察等待異步渲染完成。四個階段循環(huán)推進直到自然語言描述的所有步驟和斷言全部完成才算一次成功的執(zhí)行。這個完整過程會被記錄為操作日志和頁面快照理論上可以完整還原每一步發(fā)生了什么。2.3 回放緩存的數(shù)據(jù)結(jié)構(gòu)與匹配機制緩存不是簡簡單單把操作步驟存成一段文本它要解決“下次怎么找到這份緩存”“怎么判斷這份緩存還能不能用”的問題。一個緩存條目通常包含這么幾類關(guān)鍵信息場景標(biāo)識、操作序列、元素指紋、前置狀態(tài)指紋、資源快照。場景標(biāo)識就是一個場景的唯一ID比如根據(jù)用例文件路徑和哈希值生成一個固定ID。操作序列是每一步的原子指令例如OPEN_PAGE、FILL_INPUT、CLICK_BUTTON、WAIT_FOR、ASSERT_TEXT每一步都帶有具體參數(shù)。元素指紋用于描述目標(biāo)元素的特征不是簡單的CSS選擇器而是一組特征集合標(biāo)簽類型、可見文本、可訪問性名稱、在DOM里的相對位置、周圍文本的片段。為什么不用單一選擇器因為頁面調(diào)整層級時單一選擇器很可能失效而特征集合匹配起來容錯空間要大得多。前置狀態(tài)指紋則記錄執(zhí)行到這一步時頁面的狀態(tài)摘要比如URL、標(biāo)題、關(guān)鍵元素的文本哈希。匹配流程也挺清晰先按場景ID找緩存條目找不到就直接走LLM規(guī)劃。找到了就開始逐步驟重放。每一步都先校驗當(dāng)前頁面狀態(tài)與該步驟記錄的前置狀態(tài)指紋是否匹配URL有沒有變目標(biāo)元素還在不在關(guān)鍵文本對不對。吻合就繼續(xù)重放不吻合就立即中斷并觸發(fā)LLM降級同時把新規(guī)劃的路徑更新進緩存。這里有個度的問題。嚴苛的匹配幾乎要求每一步都與錄制時一致安全性高但容易誤報寬松的匹配只校驗核心元素存在容忍頁面無關(guān)部分的變動誤報少但偶爾會把結(jié)構(gòu)變但邏輯已變的情況放過去。項目默認是折中策略核心斷言元素必須精確校驗輔助元素允許存在缺失。緩存的更新策略也很務(wù)實。場景新走通一條分支會追加到緩存序列里重放失敗后模型修復(fù)了路徑系統(tǒng)會把舊的失敗步驟替換掉高頻失敗的步驟會自動標(biāo)記高優(yōu)先級下次優(yōu)先由模型重新規(guī)劃。這實際上形成了一個滾動的自我完善機制用得越多緩存越貼合當(dāng)前頁面。3. 實操落地從安裝到緩存生效的完整流程3.1 環(huán)境準(zhǔn)備與初始化e2e以命令行工具的形式發(fā)布Node.js運行時是基礎(chǔ)要求版本建議在18以上。瀏覽器側(cè)需要有一份可調(diào)用的瀏覽器運行時這塊大多數(shù)環(huán)境下默認就有了不需要額外配置。安裝直接用包管理器拉包就行npm install -g e2e/cli裝完先在項目里做初始化e2e initinit命令會在當(dāng)前目錄生成一份配置文件同時創(chuàng)建用例目錄和緩存目錄。默認結(jié)構(gòu)大概是my-project/ e2e-scenarios/ login.e2e.md .e2e-cache/ e2e.config.json配置文件里最需要關(guān)注的是模型接入?yún)?shù)和緩存策略{ model: { baseUrl: http://your-llm-endpoint/v1, apiKey: sk-your-key, name: your-model }, browser: { headless: false, viewport: { width: 1280, height: 800 } }, cache: { dir: .e2e-cache, mode: prefer-cache, maxAgeDays: 30 } }模型接口只要兼容主流API風(fēng)格就能用本地部署的模型也行。apiKey建議通過環(huán)境變量注入別直接寫死在配置文件里提交到倉庫。3.2 編寫第一個自然語言測試場景初始化完成后在e2e-scenarios目錄下新建一個場景文件比如login.e2e.md。一個完整的場景文件我通常分成三塊場景說明、操作步驟、斷言驗證。操作步驟是主體斷言可以在步驟中穿插也可以集中放在最后。以一個“登錄并創(chuàng)建草稿”場景為例# 登錄并創(chuàng)建草稿 ## 場景說明 用戶使用測試賬號登錄后臺進入控制臺新建一篇草稿。 ## 操作步驟 - 打開頁面https://demo.xxxxxx.com/login - 輸入框“用戶名”輸入 test_user - 輸入框“密碼”輸入 test_pass - 點擊按鈕“登錄” - 等待出現(xiàn)“控制臺首頁” - 點擊按鈕“新建草稿” - 輸入框“標(biāo)題”輸入季度總結(jié)報告 - 點擊按鈕“保存草稿” ## 斷言驗證 - 頁面顯示提示“草稿已保存” - 列表中出現(xiàn)標(biāo)題“季度總結(jié)報告” - 頁面右上角顯示當(dāng)前用戶“test_user”這里有個容易踩的坑場景描述里盡量不要寫“如果”“假設(shè)”這類條件句Agent對條件分支的處理能力雖然強但會讓執(zhí)行鏈路變長、出錯概率變大。一個文件專注一條主流程分支和異常路徑單獨建場景文件排查起來也清晰。3.3 首次運行錄制與緩存重放驗證場景文件建好后第一次運行加上錄制標(biāo)記e2e run scenarios/login.e2e.md --record首跑過程中Agent會一步步規(guī)劃并執(zhí)行操作控制臺會打印每一步的來源這一步是由模型實時規(guī)劃出來的還是一開始的意圖解析以及執(zhí)行結(jié)果是否通過。首次跑完日志里能看到類似這樣的輸出[1/8] OPEN_PAGE - EXEC http://your-llm-endpoint 已訪問頁面標(biāo)題正確 [2/8] FILL_INPUT - EXEC 用戶名已輸入值回顯正確 [3/8] CLICK_BUTTON - EXEC 登錄按鈕已點擊等待頁面跳轉(zhuǎn) [4/8] WAIT_FOR - EXEC 已等待到“控制臺首頁” ... [8/8] ASSERT_TEXT - EXEC 斷言通過“草稿已保存”錄制成功意味著這一整套可執(zhí)行的操作序列已經(jīng)被寫入了緩存目錄。之后不錄緩存運行命中緩存時日志會明顯不同每一步旁邊會出現(xiàn)HIT_CACHE標(biāo)記[1/8] OPEN_PAGE - CACHE_HIT [2/8] FILL_INPUT - CACHE_HIT [3/8] CLICK_BUTTON - CACHE_HIT [4/8] ASSERT_TEXT - CACHE_HIT從HIT_CACHE這一步開始模型就沒有被調(diào)用過。想確認模型是否真的沒被調(diào)用可以看兩個指標(biāo)一是執(zhí)行耗時首跑登錄場景通常需要幾十秒二次命中緩存后能壓到一兩秒量級完全不一樣二是配置文件里的模型調(diào)用日志或token計數(shù)如果二次運行后沒有新增請求記錄就說明整個流程走的是純回放。3.4 緩存治理與日常維護命令項目提供了幾個比較順手的緩存管理命令# 查看緩存狀態(tài)包括命中率和緩存條目 e2e cache status # 清理全部緩存 e2e cache clean # 強制某個場景重新走模型錄制 e2e run scenarios/login.e2e.md --record --force緩存默認目錄是.e2e-cache它里面的內(nèi)容可以放進Git倉庫也可以忽略掉。放進去的好處是團隊所有人都能共享已錄制好的緩存省去各自首跑的成本缺點是頁面變動后大家得記得同步刷新。我的建議是項目主倉庫忽略緩存另外單獨維護一份緩存的話用CI定期跑一次強刷生成。如果項目跑的速度很快但偶爾會莫名失敗先別懷疑是緩存問題看看是不是執(zhí)行環(huán)境不一致。本地瀏覽器和CI環(huán)境字體、分辨率差異可能導(dǎo)致元素定位結(jié)果不同。項目里配置viewport統(tǒng)一成一個固定尺寸能有效降低這類差異。3.5 多場景編排與CI集成單場景跑通只是第一步真實項目里得把多個場景串起來才能進回歸流程。項目支持在一個文件里引用多個場景也支持批量跑一個目錄下所有場景文件e2e run scenarios/ --record批量模式下緩存就非常關(guān)鍵了。核心高頻場景全部走緩存新增或改動過的場景走模型整體回歸時間可以被壓到一個很可觀的量級。CI里我建議這樣配置push觸發(fā)的快回歸只跑緩存不允許模型調(diào)用最大程度保證速度每天定時任務(wù)跑一次全量回歸允許模型降級頁面結(jié)構(gòu)變化時能自動修復(fù)路徑并刷新緩存同時把修復(fù)的diff推給相關(guān)人。這種組合配下來既穩(wěn)又省。4. 常見問題與排障實錄4.1 頁面改版后緩存大量失效現(xiàn)象某次前端重構(gòu)后跑回歸時日志里大面積出現(xiàn)CACHE_MISS很多場景回到模型驅(qū)動速度和成本又回到了老水平。排查方向先區(qū)分是“頁面結(jié)構(gòu)變了”還是“業(yè)務(wù)邏輯變了”。結(jié)構(gòu)變化比如按鈕位置調(diào)整、層級變化、文案微調(diào)這種只需要讓系統(tǒng)進入寬松匹配模式核心元素檢測到了就直接重放很快能恢復(fù)大量緩存命中。邏輯變化比如登錄流程從兩步變成三步這種必須重新錄制千萬不要強行沿用舊緩存。我的操作習(xí)慣每次發(fā)布前主動跑一次全量錄制把緩存刷新到最新頁面狀態(tài)。與其等失敗降級不如提前主動重錄。4.2 動態(tài)文本與驗證碼類場景動態(tài)文本問題頁面里有時間戳、隨機訂單號、當(dāng)前日期這類每次都不一樣的文本。如果斷言寫死“訂單編號為2024001”重放時會因為實際值是動態(tài)生成的對不上而失敗。處理辦法斷言動態(tài)文本時描述“存在”“出現(xiàn)”而不是“等于某個具體值”。例如寫“頁面出現(xiàn)‘訂單號’字樣”或“成功提示包含‘已提交’”這類相對普適的斷言就能避開這個問題。驗證碼是另一個麻煩。這種場景我建議直接在測試環(huán)境關(guān)閉驗證碼或者使用測試專用的萬能驗證碼。如果實在不行至少在自然語言里注明“跳過驗證碼校驗”讓Agent知道這是無需驗證的環(huán)節(jié)不要被卡住反復(fù)等待。4.3 Agent點錯元素與斷言誤報現(xiàn)象元素有多個匹配Agent選了第一個但那個其實不是用戶習(xí)慣操作的目標(biāo)。斷言通過但測試結(jié)果失真。這類問題多發(fā)于頁面上有重復(fù)文案、重復(fù)按鈕時。比如列表頁每個表格行都有一個“編輯”按鈕Agent只根據(jù)“點擊編輯”這句描述去定位很容易點錯行。定位方法一是看項目生成的執(zhí)行快照每一個步驟落盤時都有操作前后的頁面截圖翻一眼截圖就知道它點到了哪里二是把描述寫得更精準(zhǔn)給出行號或位置作為補充條件例如“點擊第三行右側(cè)的‘編輯’按鈕”三是調(diào)整斷言粒度不只斷言“編輯成功”還斷言“打開的是對應(yīng)編號的記錄”。4.4 緩存正確性與過期風(fēng)險緩存最讓人擔(dān)心的不是“沒用上”而是“用錯了”頁面結(jié)構(gòu)看起來一樣但背后的業(yè)務(wù)邏輯已經(jīng)變了舊緩存依舊會跑通測試變成擺設(shè)。這是我強調(diào)最多的一點緩存只是一條已探明的路徑不是業(yè)務(wù)正確性的證明。它最適合用在回歸驗證環(huán)節(jié)用來快速確認“這次改動沒有影響主要流程”不適合作為發(fā)布前的唯一驗收手段。核心業(yè)務(wù)場景定期強刷緩存跑一遍模型重新校驗仍然非常有必要。可以給緩存設(shè)置有效期比如maxAgeDays設(shè)成30天超過期限的緩存條目自動失效。這個期限切合項目發(fā)布節(jié)奏來定就好。4.5 模型調(diào)用與成本觀察項目運行一段時間后可以通過cache status看命中率。我會關(guān)注兩個數(shù)字場景命中率和步驟命中率。場景命中率是有多少場景完全走了緩存步驟命中率是混合場景里有多少步走了緩存。實測下來頁面結(jié)構(gòu)迭代不頻繁的項目核心高頻場景穩(wěn)定后步驟命中率能做到九成以上。剩余那不到一成的模型調(diào)用基本都集中在新增頁面和新場景上token開銷可以控制到很低的水平。如果你的項目命中率一直很低先別怪緩存機制大概率是頁面改版太頻繁或者說每個用例跑的目標(biāo)頁面結(jié)構(gòu)差異太大。這時候可以犧牲一點速度把cache mode調(diào)成prefer-llm讓模型實時規(guī)劃為主緩存只作為可選加速項這個策略會明顯更貼合頻繁改版的項目階段。我個人在這一年多實踐里的最大體會是這個工具真正改變的是測試的書寫方式和維護心態(tài)。以前寫E2E我寫的是“怎么操作”現(xiàn)在寫e2e場景文件我寫的是“我想驗證什么”。頁面結(jié)構(gòu)的變化交給緩存失效和自動降級去處理我的注意力可以放回業(yè)務(wù)邏輯本身。如果你也被一堆脆弱的選擇器折磨得夠嗆手頭又有可用的模型接口強烈建議拿一個高頻回歸場景試試這套流程。最后再分享一個小技巧錄制場景時盡量選一個干凈的初始狀態(tài)比如新建一個測試用戶、清空歷史數(shù)據(jù)。這樣錄出來的緩存才是“從零開始的完整路徑”而不是依賴某條已存在數(shù)據(jù)的半截路徑。初始狀態(tài)干凈緩存的可復(fù)用性會高出一大截。