備:EasyClick自動化腳本實戰(zhàn)與避坑指南)
1. 項目緣起與核心需求拆解1.1 為什么會有“免越獄批量控制”這個需求做移動端自動化測試或者批量運(yùn)營的同行應(yīng)該都有體會iOS 設(shè)備一旦上了規(guī)模管理成本就會指數(shù)級上升。手里攥著十幾臺甚至幾十臺 iPhone如果每臺都要人工點一遍那基本不用干別的了。早期大家走的路子無非兩條要么越獄后裝各種插件要么用 Xcode 自帶的 UI Testing 框架寫測試用例。越獄的問題很明顯系統(tǒng)版本一升級就失效而且設(shè)備狀態(tài)不穩(wěn)定很多業(yè)務(wù)場景根本不允許越獄。Xcode UI Testing 倒是官方方案但它依賴完整的開發(fā)環(huán)境跑起來重批量部署也麻煩更適合做回歸測試而不是日常的批量操作。EasyClick 這個工具切入的正是這個夾縫地帶。它的核心思路是不碰系統(tǒng)底層不依賴越獄通過蘋果官方的開發(fā)者調(diào)試通道把自動化腳本注入到目標(biāo)設(shè)備上執(zhí)行。你可以把它理解成一個“合法的外掛”——它用的是蘋果自己開放的接口只是把這些接口包裝成了更容易上手的腳本引擎。這樣一來系統(tǒng)升級不會導(dǎo)致工具失效設(shè)備也不需要解鎖批量控制的門檻就降下來了。這個項目適合誰呢我梳理了一下大概三類人最需要一是做移動端自動化測試的工程師需要批量跑用例、收集日志二是做 App 兼容性驗證的團(tuán)隊要在多臺真機(jī)上驗證 UI 表現(xiàn)三是做批量設(shè)備管理的運(yùn)維人員需要定時執(zhí)行一些重復(fù)性操作。如果你只是偶爾控制一兩臺設(shè)備那手動操作可能更快但一旦上了五臺以上這套方案的效率優(yōu)勢就非常明顯了。1.2 免越獄方案的技術(shù)邊界在哪里這里必須先把預(yù)期管理做好。免越獄不等于無所不能它的能力邊界是由蘋果開放的接口決定的。EasyClick 能做的事情包括模擬點擊、滑動、輸入文本、讀取屏幕元素、截屏、啟動和關(guān)閉 App、獲取設(shè)備基本信息等。這些操作覆蓋了絕大多數(shù)日常自動化的需求。但它做不到的事情也很明確無法修改系統(tǒng)級設(shè)置無法訪問其他 App 的私有數(shù)據(jù)無法繞過 App 自身的反調(diào)試機(jī)制。換句話說它是在蘋果允許的框架內(nèi)做自動化不是破解。這個邊界其實反而是好事。因為不碰系統(tǒng)底層所以穩(wěn)定性高不會因為系統(tǒng)小版本更新就掛掉。我實測下來從 iOS 14 到 iOS 17核心功能都能正常跑只有個別 API 的調(diào)用方式需要微調(diào)。這種穩(wěn)定性對于需要長期運(yùn)行的項目來說比什么都重要。1.3 批量控制的核心挑戰(zhàn)與應(yīng)對思路批量控制不是簡單地把單機(jī)腳本復(fù)制多份。真正的難點在于設(shè)備發(fā)現(xiàn)與連接管理、腳本的批量分發(fā)與同步執(zhí)行、執(zhí)行結(jié)果的收集與異常處理。這三件事如果做不好設(shè)備一多就會亂成一鍋粥。EasyClick 在這方面的設(shè)計思路是“中心控制分布式執(zhí)行”也就是一臺電腦作為控制端通過 USB 或者局域網(wǎng)連接多臺設(shè)備腳本在控制端編寫和調(diào)度執(zhí)行指令下發(fā)到各臺設(shè)備上并行運(yùn)行。這個架構(gòu)的好處是集中管理壞處是對控制端的性能有一定要求。我試過用一臺普通筆記本同時控制 8 臺設(shè)備CPU 占用大概在 40% 左右內(nèi)存占用 2GB 上下基本還能接受。如果設(shè)備數(shù)量超過 15 臺建議換性能更好的機(jī)器或者分多臺控制端來管理。這個數(shù)字不是絕對的跟腳本復(fù)雜度也有關(guān)系簡單點擊腳本和復(fù)雜圖像識別腳本的資源消耗完全不是一個量級。2. 環(huán)境搭建與核心工具選型2.1 控制端環(huán)境準(zhǔn)備Windows 與 macOS 的取舍EasyClick 的控制端支持 Windows 和 macOS 兩個平臺選哪個取決于你的設(shè)備連接方式。如果走 USB 連接兩個平臺差別不大如果走局域網(wǎng)連接macOS 的網(wǎng)絡(luò)穩(wěn)定性通常更好一些但 Windows 的兼容性更廣特別是涉及到一些老款設(shè)備的驅(qū)動問題時Windows 反而更省心。我個人的建議是如果團(tuán)隊里大部分人用 Windows那就統(tǒng)一用 Windows減少協(xié)作成本。如果是個人的項目手頭有什么就用什么不用糾結(jié)。安裝過程不復(fù)雜官網(wǎng)下載安裝包一路下一步就行。但有一個坑要注意安裝路徑不要有中文和空格否則某些底層組件會莫名其妙地報錯。這個坑我踩過排查了半天才發(fā)現(xiàn)是路徑問題。安裝完成后第一次啟動需要安裝設(shè)備驅(qū)動。Windows 上會自動彈出驅(qū)動安裝提示跟著走就行。macOS 上一般不需要額外裝驅(qū)動系統(tǒng)自帶。如果設(shè)備連接后控制端識別不到先檢查數(shù)據(jù)線再檢查設(shè)備上是否彈出了“信任此電腦”的提示。這個提示必須點信任否則控制端只能看到設(shè)備但無法執(zhí)行任何操作。2.2 設(shè)備端準(zhǔn)備開發(fā)者模式與信任設(shè)置這是整個流程里最關(guān)鍵的一步也是最容易出問題的一步。免越獄方案依賴蘋果的開發(fā)者調(diào)試通道所以設(shè)備上必須開啟開發(fā)者模式。iOS 16 之后開發(fā)者模式藏得比較深需要在“設(shè)置-隱私與安全性”里找到“開發(fā)者模式”開關(guān)打開后設(shè)備會重啟一次。重啟后還需要在彈窗里再次確認(rèn)開啟。這個步驟看起來簡單但很多人卡在這里因為開關(guān)打開后如果不重啟或者重啟后沒確認(rèn)控制端就是連不上。另外設(shè)備上需要安裝一個配套的助手 App這個 App 的作用是建立控制端和設(shè)備之間的通信橋梁。安裝方式通常是通過控制端推送不需要手動下載。安裝完成后需要在“設(shè)置-通用-設(shè)備管理”里信任對應(yīng)的開發(fā)者證書。這一步不做的話App 打不開會提示“未受信任的開發(fā)者”。信任之后助手 App 就能正常啟動了。注意開發(fā)者模式開啟后設(shè)備的安全性會略有降低這是蘋果官方的設(shè)計不是工具的問題。如果設(shè)備涉及敏感數(shù)據(jù)建議用專門的測試機(jī)來跑自動化不要用主力機(jī)。2.3 腳本引擎選型JavaScript 還是原生 APIEasyClick 支持兩種腳本編寫方式一種是 JavaScript 腳本一種是原生 API 調(diào)用。JavaScript 的優(yōu)勢是上手快語法靈活社區(qū)里能找到大量現(xiàn)成的代碼片段可以參考。原生 API 的優(yōu)勢是執(zhí)行效率高能調(diào)用一些 JavaScript 層沒有暴露的底層功能。我的建議是日常的點擊、滑動、輸入操作用 JavaScript 就夠了開發(fā)效率高調(diào)試也方便。如果涉及到復(fù)雜的圖像識別或者需要精確控制時序的場景再考慮用原生 API。兩者可以在同一個項目里混用不是非此即彼的關(guān)系。我自己的項目里主流程用 JavaScript 寫個別性能瓶頸的地方用原生 API 重寫整體效率能提升 30% 左右。腳本編輯器的功能比較完善有語法高亮、自動補(bǔ)全、斷點調(diào)試這些基礎(chǔ)功能。調(diào)試的時候可以單步執(zhí)行觀察每一步的設(shè)備狀態(tài)變化。這個功能在排查問題時非常有用比盲猜強(qiáng)太多了。3. 核心腳本編寫與批量控制實現(xiàn)3.1 單機(jī)腳本的編寫要點與調(diào)試技巧寫單機(jī)腳本是整個項目的基礎(chǔ)。腳本的核心邏輯通常包括啟動目標(biāo) App、等待界面加載、定位元素、執(zhí)行操作、驗證結(jié)果。這里面最容易出問題的是元素定位和等待時機(jī)。元素定位的方式有好幾種按坐標(biāo)點擊、按文本查找、按控件 ID 查找、按圖像識別查找。按坐標(biāo)點擊最簡單但最不穩(wěn)定因為不同分辨率、不同系統(tǒng)版本的界面布局可能不一樣。按文本查找相對穩(wěn)定但前提是界面上的文本是固定的。按控件 ID 查找最準(zhǔn)確但需要 App 開發(fā)者給控件設(shè)置了可訪問性標(biāo)識很多 App 并沒有做這件事。按圖像識別查找最通用但速度慢而且受屏幕亮度、色彩偏差影響。我通常的策略是優(yōu)先用控件 ID找不到就用文本再找不到才用圖像識別坐標(biāo)點擊只作為最后的兜底方案。這樣組合下來腳本的穩(wěn)定性會好很多。等待時機(jī)也很關(guān)鍵。很多新手寫腳本喜歡用固定延時比如sleep(2000)等兩秒。這種做法在簡單場景下能用但設(shè)備一多每臺設(shè)備的響應(yīng)速度不一樣固定延時要么不夠要么浪費(fèi)。更好的做法是用條件等待比如“等待某個元素出現(xiàn)最多等 10 秒”。EasyClick 提供了這類等待函數(shù)用起來很方便。// 等待元素出現(xiàn)最多等 10 秒 var element waitForElement(登錄按鈕, 10000); if (element) { element.click(); } else { log(登錄按鈕未找到可能頁面加載失敗); }調(diào)試的時候我習(xí)慣在關(guān)鍵步驟后加截屏這樣出問題時能直觀看到當(dāng)時的界面狀態(tài)。截屏文件按時間戳命名方便回溯。這個習(xí)慣幫我省了很多排查時間。3.2 批量控制的架構(gòu)設(shè)計與設(shè)備分組策略單機(jī)腳本跑通之后下一步就是批量控制。EasyClick 的批量控制界面可以同時管理多臺設(shè)備每臺設(shè)備的狀態(tài)在線、離線、執(zhí)行中、空閑一目了然。設(shè)備列表支持分組這個功能很實用。我通常按設(shè)備型號或者系統(tǒng)版本分組因為不同型號的界面布局可能有差異分組后可以針對每組寫不同的腳本分支。批量執(zhí)行的時候有兩種模式同步執(zhí)行和異步執(zhí)行。同步執(zhí)行是所有設(shè)備同時開始適合需要嚴(yán)格對齊時間的場景。異步執(zhí)行是各設(shè)備獨立開始適合執(zhí)行時間較長、不需要嚴(yán)格同步的場景。我大部分時候用異步執(zhí)行因為設(shè)備性能有差異同步執(zhí)行反而會導(dǎo)致快的設(shè)備等慢的設(shè)備整體效率下降。設(shè)備分組還有一個好處是方便做灰度發(fā)布。新腳本先在一組設(shè)備上跑確認(rèn)沒問題再推送到全部設(shè)備。這個流程在批量操作里非常重要因為一旦腳本有 bug全量推送可能導(dǎo)致所有設(shè)備都卡住恢復(fù)起來很麻煩。3.3 腳本分發(fā)與執(zhí)行結(jié)果收集的實操流程腳本分發(fā)的過程是這樣的在控制端編輯好腳本選擇目標(biāo)設(shè)備分組點擊“推送并執(zhí)行”??刂贫藭涯_本文件傳輸?shù)礁髋_設(shè)備上然后觸發(fā)執(zhí)行。執(zhí)行過程中控制端會實時接收設(shè)備回傳的日志和狀態(tài)信息。結(jié)果收集方面EasyClick 提供了日志匯總功能所有設(shè)備的執(zhí)行日志會集中顯示在控制端的日志面板里。日志可以按設(shè)備篩選也可以按時間篩選。我通常會把日志導(dǎo)出成文件方便后續(xù)分析。如果腳本執(zhí)行失敗日志里會有詳細(xì)的錯誤信息包括失敗步驟、錯誤類型、當(dāng)時的界面截圖。這些信息對于排查問題非常有用。有一個細(xì)節(jié)要注意批量執(zhí)行的時候設(shè)備回傳的數(shù)據(jù)量可能比較大如果控制端的網(wǎng)絡(luò)帶寬不夠日志可能會丟失。我遇到過這種情況后來把日志級別調(diào)低了一些只記錄關(guān)鍵信息問題就解決了。如果確實需要完整日志建議用有線網(wǎng)絡(luò)連接控制端無線網(wǎng)絡(luò)在數(shù)據(jù)量大時不太穩(wěn)定。4. 常見問題排查與避坑經(jīng)驗4.1 設(shè)備連接失敗的排查思路設(shè)備連接失敗是最常見的問題原因可能有很多。我整理了一個排查順序按這個順序走基本能定位到問題。排查步驟檢查內(nèi)容常見問題1數(shù)據(jù)線是否完好線材老化導(dǎo)致接觸不良2設(shè)備是否彈出信任提示未點信任或提示未出現(xiàn)3開發(fā)者模式是否開啟iOS 16 需要重啟并確認(rèn)4助手 App 是否已安裝并信任證書未信任導(dǎo)致 App 打不開5控制端驅(qū)動是否正常Windows 上驅(qū)動未安裝或沖突6USB 端口是否正常前置端口供電不足換后置端口這個順序是從簡單到復(fù)雜排列的大部分問題在前三步就能解決。如果走到第六步還不行那可能是設(shè)備本身的問題換一臺設(shè)備試試。提示如果設(shè)備連接后頻繁掉線優(yōu)先檢查數(shù)據(jù)線。我遇到過一根線看起來完好但內(nèi)部斷了一根芯導(dǎo)致連接極不穩(wěn)定。換線之后問題立刻消失。這種問題最難排查因為表面上看不出任何異常。4.2 腳本執(zhí)行不穩(wěn)定的典型場景與解法腳本執(zhí)行不穩(wěn)定通常表現(xiàn)為有時候能跑通有時候跑不通或者跑著跑著就卡住了。這類問題的根源往往是等待時機(jī)不對或者元素定位不準(zhǔn)確。一個典型的場景是App 啟動后有一個開屏廣告廣告出現(xiàn)的時間不固定有時候 2 秒有時候 5 秒。如果腳本用固定延時等 3 秒那廣告 5 秒的時候就會點錯位置。解法是用條件等待等廣告消失后再繼續(xù)。EasyClick 里可以用waitForElementDisappear來實現(xiàn)。另一個場景是列表滑動加載更多的時候滑動速度太快導(dǎo)致內(nèi)容還沒加載出來就繼續(xù)滑了。解法是在每次滑動后加一個短暫的等待或者等待某個加載指示器消失。這個等待時間不用太長500 毫秒到 1 秒就夠了但必須有。還有一個容易被忽略的點是設(shè)備性能差異。同樣的腳本在新設(shè)備上跑得飛快在老設(shè)備上就卡頓。如果腳本里全是固定延時老設(shè)備上就會出錯。解法是把固定延時改成條件等待讓腳本自己適應(yīng)設(shè)備速度。4.3 批量執(zhí)行時的資源競爭與性能優(yōu)化批量執(zhí)行的時候控制端的資源消耗會明顯上升。如果同時跑 10 臺以上的設(shè)備控制端的 CPU 和內(nèi)存可能成為瓶頸。我試過幾種優(yōu)化方式效果比較明顯的有兩個。第一個是降低截屏頻率。截屏是資源消耗大戶如果腳本里頻繁截屏控制端要處理大量圖像數(shù)據(jù)。我的做法是只在關(guān)鍵步驟截屏比如操作失敗時、驗證結(jié)果時其他時候不截。這樣能把截屏帶來的開銷降低 70% 以上。第二個是錯峰執(zhí)行。如果所有設(shè)備同時啟動 App控制端的瞬時負(fù)載會很高。我通常讓設(shè)備分批啟動比如每 2 秒啟動一臺這樣負(fù)載曲線會平滑很多。雖然整體執(zhí)行時間會稍微長一點但穩(wěn)定性提升明顯不會出現(xiàn)設(shè)備掉線或者腳本卡死的情況。另外控制端的日志級別也建議調(diào)整一下。調(diào)試階段可以用詳細(xì)日志正式跑的時候用簡潔日志只記錄關(guān)鍵節(jié)點和錯誤信息。這樣能減少日志寫入帶來的 IO 壓力。4.4 免越獄方案的局限性與替代思路前面說過免越獄方案的能力邊界是由蘋果接口決定的。有些操作它確實做不到比如修改系統(tǒng)時間、模擬地理位置、攔截網(wǎng)絡(luò)請求。如果項目里確實需要這些功能那免越獄方案就不適用得考慮其他路子。替代思路有幾個方向一是用蘋果官方的 XCUITest 框架功能更底層但開發(fā)成本高二是用云真機(jī)平臺按需租用設(shè)備省去本地維護(hù)的麻煩但長期成本高三是針對特定需求寫原生 App 來做靈活度最高但開發(fā)周期長。選擇哪種方案取決于項目的具體需求和資源情況。我個人的經(jīng)驗是如果 80% 的需求免越獄方案能覆蓋那就用免越獄方案剩下的 20% 用其他方式補(bǔ)。不要為了 20% 的需求把整個方案換掉那樣得不償失?;旌戏桨竿亲顒?wù)實的。5. 進(jìn)階技巧與效率提升實踐5.1 腳本模塊化與代碼復(fù)用腳本寫多了之后會發(fā)現(xiàn)很多代碼是重復(fù)的。比如登錄操作、滑動查找、等待加載這些邏輯在多個腳本里都會用到。如果每次都復(fù)制粘貼維護(hù)起來會很痛苦。我的做法是把這些通用邏輯封裝成函數(shù)庫放在單獨的腳本文件里其他腳本通過引用文件來調(diào)用。EasyClick 支持腳本引用語法很簡單在腳本開頭加一行import common.js就行。這樣 common.js 里的函數(shù)就能在當(dāng)前腳本里直接用了。這個機(jī)制讓代碼復(fù)用變得很方便改一處就能影響所有引用它的腳本。我通常會把函數(shù)庫分成幾個模塊設(shè)備操作模塊點擊、滑動、輸入、元素查找模塊按文本、按 ID、按圖像、流程控制模塊等待、重試、條件判斷、日志模塊記錄、截屏、上報。每個模塊一個文件結(jié)構(gòu)清晰找起來也方便。5.2 定時任務(wù)與無人值守的配置方法批量控制的一個典型場景是定時執(zhí)行比如每天凌晨跑一遍數(shù)據(jù)采集或者每小時檢查一次設(shè)備狀態(tài)。EasyClick 本身沒有內(nèi)置定時任務(wù)功能但可以借助操作系統(tǒng)的計劃任務(wù)來實現(xiàn)。Windows 上用“任務(wù)計劃程序”macOS 上用“l(fā)aunchd”或者“cron”。配置思路是一樣的創(chuàng)建一個定時任務(wù)到點后啟動 EasyClick 的控制端加載指定的腳本執(zhí)行完成后自動退出。這里有一個細(xì)節(jié)要注意控制端啟動后需要一定時間才能識別到設(shè)備所以腳本里最好加一個等待設(shè)備就緒的邏輯不要一啟動就立刻執(zhí)行操作。無人值守的時候異常處理尤為重要。我的做法是在腳本里加全局異常捕獲一旦出錯就記錄日志并截屏然后繼續(xù)執(zhí)行下一個任務(wù)而不是直接退出。這樣即使某個環(huán)節(jié)出問題整體流程也不會中斷。第二天來看日志就知道哪里出了問題。5.3 多設(shè)備并行時的日志管理與問題回溯設(shè)備一多日志就會變得很亂。如果所有設(shè)備的日志混在一起排查問題的時候根本分不清哪條日志是哪臺設(shè)備的。我的做法是在日志里加上設(shè)備標(biāo)識比如設(shè)備名稱或者序列號。這樣篩選日志的時候就能按設(shè)備過濾清晰很多。日志的存儲也要有規(guī)劃。我通常按日期建文件夾每天一個文件夾里面按設(shè)備建子文件夾每個設(shè)備的日志單獨存一個文件。這樣回溯的時候先定位到日期再定位到設(shè)備很快就能找到需要的日志。日志文件不要無限增長我一般保留最近 30 天的日志更早的自動清理避免占用太多磁盤空間。還有一個技巧是給日志加級別標(biāo)記比如 INFO、WARN、ERROR。排查問題的時候先看 ERROR再看 WARN最后才看 INFO。這樣能快速定位到關(guān)鍵信息不用在大量正常日志里大海撈針。5.4 從單機(jī)到批量我的實際項目演進(jìn)過程最后分享一下我自己的項目演進(jìn)過程可能對正在規(guī)劃類似項目的同行有參考價值。最開始我只控制兩臺設(shè)備腳本也是隨手寫的能用就行。后來設(shè)備增加到五臺發(fā)現(xiàn)手動管理太累就開始做設(shè)備分組和批量執(zhí)行。再后來設(shè)備到了十五臺控制端性能成了瓶頸于是做了錯峰執(zhí)行和日志優(yōu)化?,F(xiàn)在穩(wěn)定在二十臺左右每天定時跑任務(wù)基本不需要人工干預(yù)。這個過程中最大的體會是不要一開始就追求完美架構(gòu)先跑通單機(jī)再逐步擴(kuò)展到批量。每一步擴(kuò)展都會暴露新的問題解決這些問題本身就是項目的一部分。如果一開始就設(shè)計一個復(fù)雜的架構(gòu)很可能因為過度設(shè)計而遲遲跑不起來反而浪費(fèi)時間。另外腳本的健壯性比功能豐富更重要。一個能穩(wěn)定跑 100 次的簡單腳本比一個功能強(qiáng)大但跑 10 次就出錯一次的復(fù)雜腳本有價值得多。在批量場景下穩(wěn)定性就是效率。這個項目后續(xù)還可以往幾個方向擴(kuò)展一是接入消息通知腳本執(zhí)行完成后自動推送結(jié)果到手機(jī)或者郵箱二是做執(zhí)行數(shù)據(jù)的可視化把每次執(zhí)行的成功率、耗時、錯誤分布用圖表展示出來三是支持遠(yuǎn)程控制不在現(xiàn)場也能管理和調(diào)度設(shè)備。這些擴(kuò)展都不難等有需求的時候再逐步加上去就行。