試自動(dòng)化代碼直接合入:框架上下文是關(guān)鍵)
過(guò)去大半年我一直在琢磨一件事怎么讓AI真正進(jìn)到測(cè)試自動(dòng)化的執(zhí)行鏈路里而不是只在對(duì)話框里寫個(gè)demo給人看。最開始我也試過(guò)最省事的辦法——直接把需求扔給AI讓它“寫一個(gè)登錄腳本”結(jié)果它確實(shí)寫了寫得還像模像樣但一提交代碼評(píng)審問題就全出來(lái)了沒用頁(yè)面對(duì)象、等待全靠sleep、定位符寫絕對(duì)XPath甚至直接new了一個(gè)新的driver實(shí)例。負(fù)責(zé)review的同事看完一臉懵說(shuō)這代碼根本進(jìn)不了我們的框架。后來(lái)我慢慢想明白一個(gè)道理AI不是不會(huì)寫代碼是你沒把它放進(jìn)你家的規(guī)矩里。這篇文章是“AI for Testing 提效實(shí)戰(zhàn)”系列里執(zhí)行自動(dòng)化的第一篇不聊概念就聊怎么讓AI在你的框架內(nèi)產(chǎn)出能直接合入的腳本。1. AI生成的代碼為什么總是和你的工程格格不入1.1 看起來(lái)能跑的腳本實(shí)際上是一堆技術(shù)債你在AI聊天框里要一個(gè)“登錄并把用戶名拼到首頁(yè)上”的UI自動(dòng)化用例它大概率給你類似這樣的東西from selenium import webdriver import time driver webdriver.Chrome() driver.get(https://demo-app.example.com/login) driver.find_element_by_id(username).send_keys(tester) driver.find_element_by_id(password).send_keys(123456) driver.find_element_by_xpath(/html/body/div[1]/div/form/button).click() time.sleep(3) assert 歡迎tester in driver.page_source driver.quit()單看這段代碼邏輯沒錯(cuò)放到一個(gè)一次性爬蟲腳本里完全能跑。但如果你所在的是一個(gè)多人協(xié)作、要長(zhǎng)期維護(hù)的自動(dòng)化測(cè)試工程它幾乎是不可合入的。原因很直接工程里已經(jīng)規(guī)定所有頁(yè)面操作都要封裝到Page Object里driver實(shí)例統(tǒng)一由框架初始化并管理等待必須走框架封裝好的顯式等待方法定位符優(yōu)先使用測(cè)試專用屬性。而AI生成的代碼把這些全忽略了。有人可能會(huì)說(shuō)這不就是讓AI生成完再人肉改一改嘛也能用。但“改一改”的成本往往比從零寫還高。尤其是當(dāng)你一個(gè)月要生成幾十上百條用例時(shí)每條都要重構(gòu)一遍那AI的價(jià)值就只剩個(gè)“偽代碼起草器”了。所以核心問題不在AI寫得好不好而在于你讓它開工之前有沒有把“你們家的代碼長(zhǎng)什么樣、有什么規(guī)矩”告訴它。1.2 AI并不是不聰明而是沒有你頭腦里的“框架心智”大模型在訓(xùn)練時(shí)看過(guò)海量代碼它對(duì)通用編碼風(fēng)格非常熟悉包括公開教程里最常見的直接new driver、time.sleep、find_element_by_xpath那一套。但它不知道的是你們團(tuán)隊(duì)的BasePage封裝了什么、用例層能做什么不能做什么、失敗截圖是誰(shuí)負(fù)責(zé)處理的、測(cè)試數(shù)據(jù)從哪里讀。這些信息不會(huì)出現(xiàn)在任何公開代碼庫(kù)里而是你所在工程多年迭代沉淀下來(lái)的隱性約定。我把這些約定稱為“框架心智”。老測(cè)試開發(fā)在寫用例時(shí)腦子里會(huì)下意識(shí)地調(diào)用這些約定就像老司機(jī)開車不需要思考檔位和剎車。但AI默認(rèn)沒有這套心智你不主動(dòng)喂給它它就只能按通用套路來(lái)而通用套路和團(tuán)隊(duì)標(biāo)準(zhǔn)之間隔著一大段代碼評(píng)審的距離。所以“讓AI在你的框架里寫”這句話字面上就是第一步要做的把項(xiàng)目結(jié)構(gòu)、核心接口、參考用例、運(yùn)行方式、禁用項(xiàng)整理成一份它能直接讀取的上下文。這一步做扎實(shí)了后面才有“直接合入”的可能性。2. 把“框架上下文”整理成提示詞是合入的第一步2.1 上下文包的五類材料我試過(guò)很多種喂AI的方式最后收斂下來(lái)一份高質(zhì)量的上下文包只需要包含五類材料。不需要把整個(gè)倉(cāng)庫(kù)塞給它也不需要寫幾百行的框架源碼重點(diǎn)是讓它知道“怎么寫才像我們自己人”。第一類是目錄結(jié)構(gòu)節(jié)選。不需要完整列出只展示用例相關(guān)的三層核心層級(jí)就夠了比如project/ ├── pages/ # Page Object頁(yè)面元素與操作封裝在這里 ├── cases/ # 測(cè)試用例只負(fù)責(zé)業(yè)務(wù)流程與斷言 ├── utils/ # 公共工具比如數(shù)據(jù)生成、配置讀取 └── runner.py # 執(zhí)行入口第二類是核心基類接口。把頁(yè)面對(duì)象和用例基類里常用的方法簽名給出來(lái)AI才知道哪些動(dòng)作是現(xiàn)成的不需要自己造輪子class BasePage: def click(self, locator: tuple) - None def input_text(self, locator: tuple, text: str) - None def wait_until_visible(self, locator: tuple, timeout: int 10) - WebElement def get_text(self, locator: tuple) - str class BaseCase: def login(self, username: str, password: str) - HomePage第三類是2到3條已經(jīng)合入的參考用例。最好挑典型的表單填寫、列表校驗(yàn)、按鈕跳轉(zhuǎn)各一條。AI很擅長(zhǎng)模仿給它一個(gè)高質(zhì)量樣例它會(huì)照著風(fēng)格寫比給它一堆抽象規(guī)則更有用。第四類是運(yùn)行方式與測(cè)試數(shù)據(jù)約定。比如命令行入口是什么、測(cè)試數(shù)據(jù)放在哪個(gè)目錄、測(cè)試結(jié)束是否需要自動(dòng)清理。這些不屬于代碼語(yǔ)法但決定生成出來(lái)的用例能不能直接接進(jìn)執(zhí)行鏈路。第五類是明確禁用項(xiàng)。這條最重要。把你們歷史上踩過(guò)的典型坑直接寫成“禁止”比如禁止time.sleep、禁止絕對(duì)XPath、禁止在用例層直接寫locator、禁止調(diào)用未封裝的外部資源。AI是概率模型它的“默認(rèn)行為”來(lái)自海量訓(xùn)練數(shù)據(jù)只有用明確的負(fù)面清單才能壓過(guò)那些默認(rèn)行為。2.2 一套可以直接抄的Prompt框架材料準(zhǔn)備好之后每次生成用例時(shí)我會(huì)用下面這個(gè)提示詞框架它把剛才說(shuō)的內(nèi)容都?jí)嚎s成一段結(jié)構(gòu)化的約束你是我團(tuán)隊(duì)的測(cè)試開發(fā)工程師。請(qǐng)按照當(dāng)前工程約定編寫一條UI自動(dòng)化測(cè)試用例。 目標(biāo)場(chǎng)景{具體操作步驟與業(yè)務(wù)預(yù)期} 工程結(jié)構(gòu) {pages/ 與 cases/ 目錄結(jié)構(gòu)節(jié)選} 核心接口 {BasePage 與 BaseCase 方法簽名} 參考用例 {一條已合入標(biāo)準(zhǔn)的用例代碼} 運(yùn)行方式 python runner.py --case {case_name} 硬性約束 1. 頁(yè)面元素定位必須寫在 pages/ 下對(duì)應(yīng)的Page類中cases/ 里只允許調(diào)用Page對(duì)象方法 2. 禁止使用 time.sleep等待統(tǒng)一調(diào)用 wait_until_visible 或 wait_until_clickable 3. 禁止使用絕對(duì)XPath元素定位優(yōu)先使用專屬 test-id 或相對(duì)定位策略 4. 不要打印日志、不要直接截圖日志與截圖由框架統(tǒng)一處理 5. 輸出必須是完整可運(yùn)行的代碼不要額外解釋 請(qǐng)給出 cases/ 下用例文件路徑與完整代碼。這個(gè)Prompt的核心是“場(chǎng)景描述 框架約束 參考樣式”。場(chǎng)景描述要具體到“進(jìn)入列表頁(yè)點(diǎn)擊第一行的編輯按鈕修改狀態(tài)為啟用保存后斷言提示成功”而不是“測(cè)一下編輯功能”。約束越具體AI越不會(huì)自由發(fā)揮。2.3 上下文不需要“全量源碼”給接口和約定就夠了剛開始有同事會(huì)把整個(gè)項(xiàng)目代碼打包成一個(gè)文件喂給AI覺得信息越全越準(zhǔn)。實(shí)際效果恰恰相反。上下文太長(zhǎng)時(shí)模型注意力會(huì)被大量無(wú)關(guān)實(shí)現(xiàn)稀釋最后產(chǎn)出的代碼反而偏離約束。你只需要給“支撐這一個(gè)用例的最小上下文”——它要調(diào)用的接口、要遵循的寫法、要模仿的樣例。這就像新員工入職不需要讀全公司代碼庫(kù)只需要先了解自己崗位涉及的模塊和相關(guān)規(guī)范干幾天活自然就上手了。這個(gè)思路也直接決定了真實(shí)成本。大模型按token計(jì)費(fèi)動(dòng)不動(dòng)喂全量源碼既貴又慢。用精簡(jiǎn)上下文單次生成消耗很小而且迭代反饋時(shí)也能快速調(diào)整不會(huì)因?yàn)槊看味紟б淮蠖褮v史代碼而拖慢速度。我們后來(lái)甚至把上下文包按模塊拆成好幾個(gè)版本登錄測(cè)試用登錄上下文訂單測(cè)試用訂單上下文準(zhǔn)確率比一個(gè)“全量包”高出一截。3. 從AI輸出到合入分支四道檢查一個(gè)都不能少3.1 自動(dòng)檢查把“骨架一致性”交給腳本即使上下文包寫得很完善AI偶爾還是會(huì)漏掉一兩條約束。所以第一條質(zhì)量閘門應(yīng)該自動(dòng)完成不要靠人肉review去盯“是否用了sleep”這種問題。我在團(tuán)隊(duì)里做了一組靜態(tài)檢查腳本用AST解析AI生成的用例文件重點(diǎn)檢查是否 import 了 time、selenium 這類應(yīng)該由框架統(tǒng)一管理的模塊用例類中是否直接出現(xiàn)定位元組或XPath字符串是否調(diào)用了 pages/ 下已經(jīng)存在的頁(yè)面對(duì)象方法是否在斷言前使用了框架要求的顯式等待。這些檢查規(guī)則不需要很復(fù)雜正則加AST就能搞定。檢查不通過(guò)時(shí)直接把報(bào)錯(cuò)信息返回給AI讓它基于報(bào)錯(cuò)再改一輪。我發(fā)現(xiàn)這一步能把“初版不經(jīng)用”的比例從七成以上降到四成以下效果立竿見影。3.2 框架內(nèi)試運(yùn)行先證明它能在你家的環(huán)境里跑靜態(tài)檢查過(guò)了不代表能跑。第二個(gè)閘門是把AI生成的用例真正放進(jìn)你的執(zhí)行框架里跑一次。我們會(huì)把新生成的用例先放到cases/ai_pending/目錄標(biāo)記為冒煙用例跑一個(gè)專門的快速冒煙任務(wù)。這個(gè)任務(wù)只覆蓋目標(biāo)用例依賴的前置數(shù)據(jù)盡量控制在5分鐘內(nèi)出結(jié)果。通過(guò)后腳本會(huì)自動(dòng)把用例移動(dòng)到正式cases/目錄失敗的話就把日志和錯(cuò)誤信息回投給AI讓它自己修正。這個(gè)“生成—運(yùn)行—回喂—再生成”的循環(huán)是整個(gè)執(zhí)行自動(dòng)化鏈路里最值錢的部分。因?yàn)锳I模型本身不具備執(zhí)行能力它無(wú)法感知“這段代碼在當(dāng)前環(huán)境下能不能跑”但你的框架可以??蚣茇?fù)責(zé)執(zhí)行和反饋AI負(fù)責(zé)根據(jù)反饋修改兩者配合才真正形成閉環(huán)。3.3 人工評(píng)審只看增量不給AI重寫試運(yùn)行通過(guò)后才算輪到人工評(píng)審。這里最容易犯的錯(cuò)誤是reviewer忍不住親自上手改代碼把AI寫的東西重寫一遍。一旦這樣做AI就變成只能生成“初稿”的工具效率提升非常有限。我的建議是人工評(píng)審只做語(yǔ)義審核給reviewer一張檢查清單業(yè)務(wù)斷言是否完備關(guān)鍵狀態(tài)有沒有校驗(yàn)測(cè)試數(shù)據(jù)是否可復(fù)用會(huì)不會(huì)污染其他用例用例名稱與模塊劃分是否符合工程規(guī)劃有沒有把測(cè)試邏輯和業(yè)務(wù)邏輯混在一起只要不滿足就把意見描述清楚再交給AI去改。比如“這里只校驗(yàn)了提交成功提示沒有校驗(yàn)列表里新增數(shù)據(jù)的展示值”AI通常會(huì)很快補(bǔ)上。人工專注業(yè)務(wù)正確性語(yǔ)法和格式問題全部交給前面的自動(dòng)檢查去管評(píng)審速度會(huì)快很多。3.4 合入之后在回歸集里觀察兩天合入分支不等于完事。執(zhí)行自動(dòng)化最怕的就是用例剛合入時(shí)能跑過(guò)兩天就flaky。我們的做法是給新合入用例設(shè)置一個(gè)觀察期在CI報(bào)告里標(biāo)記為new_case連續(xù)跑5次全量回歸全部通過(guò)后才轉(zhuǎn)成正式用例。觀察期內(nèi)一旦出現(xiàn)偶發(fā)失敗先查定位符和等待邏輯不要簡(jiǎn)單屏蔽。這個(gè)機(jī)制看著保守但能極大地減少“AI生成的用例污染回歸集”的風(fēng)險(xiǎn)。對(duì)“直接合入”這個(gè)目標(biāo)來(lái)說(shuō)前面三道閘門是讓代碼滿足工程規(guī)范第四道閘門是讓代碼滿足穩(wěn)定性要求。四道都過(guò)了AI產(chǎn)出才算真正進(jìn)入資產(chǎn)池。4. 執(zhí)行自動(dòng)化落地中真正毀掉“直接合入”的兩個(gè)隱藏殺手4.1 脆弱的定位符死在第一輪回歸執(zhí)行自動(dòng)化里AI生成用例最常死在第一輪回歸的就是元素定位。你可以在提示詞里寫一百句“請(qǐng)使用穩(wěn)定的定位符”但如果沒給出可操作的標(biāo)準(zhǔn)AI還是很容易寫出/html/body/div[2]/div[3]/form/div/input或者帶隨機(jī)后綴的id。原因還是訓(xùn)練數(shù)據(jù)大量公開腳本為了快速演示直接用絕對(duì)XPath和當(dāng)前頁(yè)面結(jié)構(gòu)。我們后來(lái)在上下文包和自動(dòng)檢查里同時(shí)加了硬性規(guī)定優(yōu)先使用>