與模擬器自動化測試協(xié)同方案:從設(shè)備池到用例分層的實(shí)踐指南)
做自動化測試的年頭久了你會發(fā)現(xiàn)一個(gè)繞不開的話題真機(jī)和模擬器到底選哪個(gè)。早幾年我還在為這個(gè)問題站隊(duì)后來被現(xiàn)實(shí)教育了一輪才明白真正該做的不是二選一而是讓它們各干各擅長的事通過一套調(diào)度邏輯協(xié)同起來。這篇內(nèi)容想分享的就是這套“真機(jī)與模擬器自動化測試協(xié)同方案”包括設(shè)備池怎么搭、用例怎么寫才能兩頭跑、小程序這類特殊場景怎么處理以及我在實(shí)際項(xiàng)目中踩過的一些坑。這套方案解決的核心問題很直接模擬器快但“假”真機(jī)真但“慢”。如果能把功能驗(yàn)證、冒煙回歸放在模擬器上快速跑把地圖、支付、推送、弱網(wǎng)這些依賴真實(shí)硬件和真實(shí)網(wǎng)絡(luò)的場景放到真機(jī)上兜底整體效率能提升一大截穩(wěn)定性也不會被犧牲。適合正在搭自動化測試平臺的中小團(tuán)隊(duì)、剛接手客戶端測試框架的測試開發(fā)以及被“用例只能在某臺設(shè)備上跑”困擾的同學(xué)們參考。1. 為什么必須協(xié)同真機(jī)與模擬器誰也替不了誰1.1 模擬器的優(yōu)勢與邊界條件模擬器的優(yōu)勢做過自動化的人都懂創(chuàng)建快、銷毀快、快照隨便打、系統(tǒng)版本隨便切。一臺普通的辦公電腦開三四個(gè)Android模擬器實(shí)例并行跑用例完全沒壓力。而且模擬器的環(huán)境是“一次性”的跑廢了直接重置不用像真機(jī)那樣擔(dān)心數(shù)據(jù)污染、賬號狀態(tài)殘留。對于純UI流程的冒煙測試、頁面跳轉(zhuǎn)邏輯、表單校驗(yàn)這類用例模擬器幾乎是完美的執(zhí)行環(huán)境。但它的邊界也很明顯。首先是硬件能力缺失GPS信號、陀螺儀、指紋、NFC、攝像頭這些傳感器模擬器要么模擬得很假要么壓根不支持。舉個(gè)例子你測一個(gè)掃碼登錄功能在模擬器里折騰半天攝像頭調(diào)用結(jié)果真機(jī)上跟模擬器完全是兩套邏輯這就不叫自動化測試叫自欺欺人。其次是網(wǎng)絡(luò)環(huán)境太“干凈”。模擬器走的是宿主機(jī)的網(wǎng)絡(luò)延遲低、抖動小弱網(wǎng)、斷網(wǎng)重連、DNS異常這些場景根本復(fù)現(xiàn)不出來。再有就是部分系統(tǒng)級行為比如系統(tǒng)升級彈窗、應(yīng)用推送通知、來電打斷模擬器里的表現(xiàn)和真機(jī)差距很大。最典型的就是微信小程序開發(fā)者工具里直接跑地圖類API會直接給你報(bào)“movetomaplocation:fail 開發(fā)者工具暫時(shí)不支持此api調(diào)試請使用真機(jī)”。這類問題不是代碼寫錯(cuò)了而是工具的能力邊界你再怎么配環(huán)境都沒用必須上真機(jī)。1.2 真機(jī)不可替代的使用場景真機(jī)的價(jià)值一句話就能說明白用戶手里拿的是什么你就在什么上面測。地圖和定位是最典型的一類。開發(fā)者工具和模擬器對地圖API的支持都有限但真機(jī)上只要授權(quán)定位權(quán)限GPS、基站、Wi-Fi定位都能真實(shí)觸發(fā)你才能驗(yàn)證“用戶從A點(diǎn)走到B點(diǎn)頁面上的距離和路線是否正確更新”這種基礎(chǔ)但關(guān)鍵的功能。支付也是同理。微信支付、支付寶支付這類場景模擬器里要么直接不支持要么走的是一套特殊調(diào)試邏輯和真實(shí)收銀臺、真實(shí)回調(diào)完全不一樣必須真機(jī)過一遍。推送和消息通知是我踩過坑的地方。模擬器上推送消息設(shè)置了就顯示感覺一切正常。結(jié)果一到真機(jī)上廠商推送通道、App進(jìn)程被殺后的離線消息、通知欄點(diǎn)擊跳轉(zhuǎn)這些邏輯全出來了。我記得有一次版本上線前安卓真機(jī)上是能收到推送的但iOS上收不到排查到最后才發(fā)現(xiàn)是證書環(huán)境配錯(cuò)了。這類問題如果只靠模擬器永遠(yuǎn)暴露不了。還有一個(gè)容易忽略的點(diǎn)性能和功耗。模擬器用的是宿主機(jī)資源CPU和內(nèi)存都比你真實(shí)手機(jī)強(qiáng)太多頁面上一個(gè)列表滾動卡不卡、冷啟動要幾秒在模擬器里測出來完全沒有參考意義。想要做啟動耗時(shí)、幀率、內(nèi)存占用、耗電這類的性能測試?yán)侠蠈?shí)實(shí)用真機(jī)而且最好是中低端真機(jī)才能暴露出用戶真實(shí)體驗(yàn)的問題。1.3 協(xié)同分層的設(shè)計(jì)思路既然模擬器和真機(jī)各有不可替代的部分那思路就清晰了不要試圖用一個(gè)環(huán)境覆蓋所有場景而是按用例特征把測試任務(wù)分層分給合適的設(shè)備去跑。我目前在用的這套分層邏輯大致是這樣日常提交代碼后的快速回歸跑在模擬器上數(shù)量多、速度快15分鐘內(nèi)把核心功能全部過一遍有問題直接打回版本發(fā)布前的全量回歸跑在真機(jī)矩陣上覆蓋主流機(jī)型、主流系統(tǒng)版本耗時(shí)可以放到晚上定時(shí)任務(wù)里跑特殊場景用例比如地圖、支付、推送、弱網(wǎng)、性能單獨(dú)打標(biāo)只在真機(jī)上執(zhí)行不走日常調(diào)度。這個(gè)設(shè)計(jì)的核心是把“執(zhí)行速度”和“環(huán)境真實(shí)度”解耦。模擬器負(fù)責(zé)提供速度真機(jī)負(fù)責(zé)提供置信度。兩種設(shè)備通過一套統(tǒng)一的調(diào)度平臺管理對測試用例來說它不關(guān)心跑在哪臺設(shè)備上只關(guān)心自己掛的標(biāo)簽匹配了什么設(shè)備。這樣寫用例的人不需要關(guān)注設(shè)備差異維護(hù)成本大大降低執(zhí)行效率也能做到整體最優(yōu)。2. 基礎(chǔ)設(shè)施搭建統(tǒng)一設(shè)備池與任務(wù)調(diào)度2.1 模擬器選型與批量初始化市面上Android模擬器不少雷電、MuMu、夜神、逍遙各有各的特點(diǎn)。我個(gè)人的經(jīng)驗(yàn)是做自動化測試優(yōu)先看三件事一是多開能力二是Shell/ADB控制是否方便三是系統(tǒng)版本覆蓋是否靈活。雷電模擬器在自動化圈子里用的比較多因?yàn)樗詭Ф嚅_管理器可以批量創(chuàng)建實(shí)例而且它的ADB端口是可控的默認(rèn)是emulator-5554這種標(biāo)準(zhǔn)端口自己寫腳本控制非常順手。MuMu模擬器對硬件兼容性好尤其是AMD平臺跑起來很穩(wěn)定。夜神模擬器也有一批忠實(shí)用戶團(tuán)隊(duì)里有熟悉哪個(gè)的就用哪個(gè)沒有必要糾結(jié)到非某一家不可。批量初始化是搭模擬器池的關(guān)鍵一步。我通常會準(zhǔn)備一個(gè)初始化腳本在一臺宿主機(jī)上批量創(chuàng)建多個(gè)模擬器實(shí)例然后統(tǒng)一配置固定分辨率比如1080x2340不同實(shí)例可以錯(cuò)開、關(guān)閉系統(tǒng)動畫窗口動畫、過渡動畫、Animator時(shí)長縮放全部設(shè)為0、開啟開發(fā)者選項(xiàng)里的“不鎖定屏幕”、配置好Appium或Airtest的服務(wù)地址。這些配置如果手動一臺一臺點(diǎn)既費(fèi)時(shí)間又容易漏腳本化以后幾分鐘就能把10臺模擬器全部準(zhǔn)備好。2.2 真機(jī)接入與設(shè)備狀態(tài)管理真機(jī)的接入和管理比模擬器麻煩得多。首先是連接方式USB直連容易受接口和線材影響大批量設(shè)備更推薦Wi-Fi調(diào)試或者通過USB hub統(tǒng)一供電和數(shù)據(jù)傳輸。設(shè)備多了以后需要一套設(shè)備管理平臺來統(tǒng)一納管自己團(tuán)隊(duì)用的話可以從簡單的方案入手維護(hù)一個(gè)設(shè)備信息表記錄每臺設(shè)備的UDID、系統(tǒng)版本、分辨率、所屬測試任務(wù)、當(dāng)前狀態(tài)空閑/占用/離線然后用腳本輪詢adb devices來上報(bào)狀態(tài)。設(shè)備狀態(tài)管理一定要做自動化的健康檢查。真機(jī)長時(shí)間掛在測試平臺上最常見的現(xiàn)象就是掉線、屏幕鎖定、應(yīng)用被系統(tǒng)回收。所以我的做法是每臺設(shè)備上常駐一個(gè)Agent每隔幾分鐘上報(bào)一次狀態(tài)一旦發(fā)現(xiàn)設(shè)備離線或者屏幕滅了就自動執(zhí)行adb reconnect或者點(diǎn)亮屏幕。還有個(gè)容易被忽略的點(diǎn)真機(jī)屏幕常亮設(shè)置。手機(jī)默認(rèn)會自動息屏自動化跑著跑著屏幕一鎖用例就全部掛在找元素上了。初始化設(shè)備的時(shí)候一定要執(zhí)行svc power stayon true并且把休眠時(shí)間設(shè)為30分鐘或永不。2.3 任務(wù)調(diào)度與設(shè)備匹配邏輯設(shè)備池建好之后核心就是任務(wù)調(diào)度這塊。我用的方案不算復(fù)雜測試任務(wù)提交到隊(duì)列調(diào)度器根據(jù)任務(wù)的設(shè)備標(biāo)簽把任務(wù)分發(fā)給對應(yīng)的worker執(zhí)行。worker從設(shè)備池里取一臺空閑設(shè)備執(zhí)行完畢后釋放。匹配邏輯關(guān)鍵在打標(biāo)。每一臺設(shè)備都有一組標(biāo)簽比如device_typeemulator、system_version12、screen_size1080x2340每一個(gè)任務(wù)也都有一組標(biāo)簽比如device_typedevice、system_version10。調(diào)度器做匹配的時(shí)候先滿足硬性標(biāo)簽再考慮負(fù)載均衡。比如一個(gè)真機(jī)任務(wù)就不要往模擬器上派一個(gè)要求系統(tǒng)版本12以下的任務(wù)就不要派給Android 14的設(shè)備。這套調(diào)度邏輯的優(yōu)點(diǎn)是設(shè)備擴(kuò)縮容很簡單。要加執(zhí)行能力往設(shè)備池里加模擬器實(shí)例或者真機(jī)就行測試代碼完全不用改。當(dāng)初我用Python寫了個(gè)簡單的輪詢調(diào)度器幾百行代碼配合Redis隊(duì)列和worker進(jìn)程就把幾十臺設(shè)備管理起來了。團(tuán)隊(duì)如果不想自己造輪子直接用Jenkins的分布式節(jié)點(diǎn)加設(shè)備插件或者用現(xiàn)成的測試平臺工具也能達(dá)到類似效果。3. 用例設(shè)計(jì)寫一套能在兩種環(huán)境穩(wěn)定跑的腳本3.1 設(shè)備能力探測與用例分支能不能寫一套用例同時(shí)在模擬器和真機(jī)上跑很大程度上取決于用例代碼里有沒有“環(huán)境感知”。我見過很多失敗的例子都在代碼里硬編碼了設(shè)備信息比如默認(rèn)屏幕是1080x1920、默認(rèn)系統(tǒng)版本是9換臺設(shè)備就掛了。所以第一原則是用例代碼不做任何設(shè)備假設(shè)一切以運(yùn)行時(shí)的能力探測為準(zhǔn)。舉個(gè)例子判斷當(dāng)前設(shè)備是模擬器還是真機(jī)我會在啟動腳本里讀取設(shè)備信息然后打到一個(gè)全局變量里供用例調(diào)用def detect_device_type(driver): # 通過系統(tǒng)屬性判斷是否為模擬器 # ro.kernel.qemu 為1通常是模擬器 try: result driver.execute_script(mobile: shell, { command: getprop, args: [ro.kernel.qemu] }) return emulator if result.strip() 1 else device except Exception: return device拿到設(shè)備類型之后用例里的差異邏輯就通過分支來處理。比如模擬器上點(diǎn)擊某個(gè)坐標(biāo)可以直接用絕對坐標(biāo)但真機(jī)上屏幕尺寸和分辨率各不相同就必須用元素定位或者百分比坐標(biāo)。再比如定位權(quán)限彈窗不同系統(tǒng)版本的授權(quán)按鈕文案不一樣Android 12和Android 8的彈窗樣式完全是兩回事用例里統(tǒng)一封裝一個(gè)授權(quán)函數(shù)內(nèi)部根據(jù)系統(tǒng)版本走不同的分支。3.2 非預(yù)期彈窗的統(tǒng)一攔截機(jī)制“自動化測試非預(yù)期彈窗導(dǎo)致失敗”這個(gè)問題我是深有體會。用例明明寫得很穩(wěn)跑著跑著一個(gè)“系統(tǒng)更新”彈窗跳出來把頁面上的元素?fù)踝×它c(diǎn)擊就穿透到彈窗上整個(gè)用例直接失敗。后來發(fā)現(xiàn)模擬器上幾乎不出現(xiàn)彈窗但真機(jī)上的彈窗來源五花八門系統(tǒng)更新、應(yīng)用內(nèi)廣告、權(quán)限申請、隱私協(xié)議、推送通知、QQ/微信的懸浮窗……我的解決方案是建立一個(gè)統(tǒng)一的彈窗攔截機(jī)制在每個(gè)關(guān)鍵操作之前執(zhí)行一次攔截函數(shù)把已知的、可能出現(xiàn)的彈窗統(tǒng)一處理掉。核心思想是維護(hù)一個(gè)“彈窗庫”每個(gè)彈窗用一組特征來識別比如包名、控件ID、文本內(nèi)容。攔截函數(shù)遍歷彈窗庫如果發(fā)現(xiàn)當(dāng)前頁面上有匹配的彈窗就執(zhí)行關(guān)閉操作。關(guān)鍵是這個(gè)彈窗庫要按設(shè)備維度區(qū)分。模擬器上不需要攔截的彈窗真機(jī)上可能需要系統(tǒng)版本不同同一個(gè)彈窗的關(guān)閉按鈕位置也不同。所以我會給彈窗庫里的每一條記錄打上適用條件比如device_typeemulator、system_version12攔截的時(shí)候根據(jù)當(dāng)前設(shè)備信息做過濾。這個(gè)機(jī)制看起來簡單但工作量主要在維護(hù)彈窗庫上每次真機(jī)上跑出新彈窗就把它補(bǔ)充進(jìn)庫里去。3.3 網(wǎng)絡(luò)與服務(wù)調(diào)用差異的處理模擬器和真機(jī)在訪問測試服務(wù)時(shí)有一個(gè)巨大的差異localhost。模擬器里訪問宿主機(jī)的服務(wù)可以用10.0.2.2這個(gè)特殊地址但真機(jī)上完全沒有這個(gè)概念你寫死在用例里的10.0.2.2在真機(jī)上根本不通。處理方式有兩種我推薦第二種。第一種是把測試服務(wù)的地址配置成局域網(wǎng)IP真機(jī)和模擬器都能訪問但需要保證設(shè)備和宿主機(jī)在同一網(wǎng)段而且公司網(wǎng)絡(luò)策略可能限制設(shè)備訪問。第二種是用adb reverse命令把設(shè)備上的端口映射到宿主機(jī)這樣代碼里可以統(tǒng)一使用localhost無需感知設(shè)備類型adb -s device_udid reverse tcp:8080 tcp:8080執(zhí)行完這條命令后設(shè)備上訪問localhost:8080就會轉(zhuǎn)發(fā)到宿主機(jī)的8080端口。這個(gè)方式對模擬器同樣有效所以是用例代碼里最省心的方案。不過需要提醒的是adb reverse在每次設(shè)備重連后都會失效所以設(shè)備初始化腳本里要重新執(zhí)行一遍。3.4 回歸策略與設(shè)備分配矩陣用例和設(shè)備都準(zhǔn)備好了接下來就是怎么分配執(zhí)行策略。我習(xí)慣按風(fēng)險(xiǎn)等級和設(shè)備特性做一個(gè)分配矩陣在測試平臺上配置好這樣每次執(zhí)行不用人工干預(yù)全自動分流。執(zhí)行矩陣大致是這個(gè)思路用例類型示例執(zhí)行設(shè)備執(zhí)行時(shí)機(jī)核心流程冒煙登錄、注冊、首頁加載模擬器每次代碼提交后功能模塊回歸搜索、下單、個(gè)人中心模擬器每日定時(shí)系統(tǒng)兼容性不同Android版本UI展示真機(jī)矩陣版本發(fā)布前硬件依賴場景地圖、相機(jī)、指紋、NFC真機(jī)版本發(fā)布前性能與弱網(wǎng)啟動耗時(shí)、頁面上滑幀率真機(jī)里程碑版本支付與推送微信支付、廠商推送真機(jī)版本發(fā)布前這個(gè)矩陣不是固定的每個(gè)團(tuán)隊(duì)可以根據(jù)自己的業(yè)務(wù)特點(diǎn)調(diào)整。核心原則是變化頻繁的用例靠近模擬器追求反饋速度依賴系統(tǒng)能力的用例靠近真機(jī)追求環(huán)境和真實(shí)性模棱兩可的用例先在模擬器上跑如果連續(xù)跑一段時(shí)間都沒有出現(xiàn)過環(huán)境差異導(dǎo)致的問題那就繼續(xù)留在模擬器沒有必要為了“儀式感”把每個(gè)用例都在真機(jī)上跑一遍。4. 小程序與跨端場景的協(xié)同細(xì)節(jié)4.1 開發(fā)者工具限制與真機(jī)兜底小程序自動化測試和App有個(gè)很大的不同開發(fā)者工具本身不是最終運(yùn)行環(huán)境它只是調(diào)試工具所以有一堆API在開發(fā)者工具里是不能用的。最常見的報(bào)錯(cuò)就是本文開頭提到的movetomaplocation:fail 開發(fā)者工具暫時(shí)不支持此api調(diào)試請使用真機(jī)還有藍(lán)牙、NFC、掃碼、錄音、攝像頭這類硬件能力API在開發(fā)者工具里基本全是“演示模式”結(jié)果不真實(shí)。處理這類場景的統(tǒng)一原則是能在開發(fā)者工具里跑的用例通常是純頁面渲染、事件綁定、請求發(fā)送在開發(fā)者工具上跑速度快、回放穩(wěn)定凡是調(diào)用硬件API或者依賴平臺能力的地方一律打標(biāo)跳真機(jī)。我在實(shí)際項(xiàng)目里的做法是給用例加一個(gè)platform標(biāo)記例如pytest.mark.real_device執(zhí)行時(shí)由調(diào)度器來分流。另外還有一個(gè)容易踩的坑真機(jī)調(diào)試需要綁定開發(fā)者賬號不是隨便拿一臺手機(jī)掃碼就能用的。如果登錄的不是小程序的開發(fā)者手機(jī)上會直接提示“登錄用戶不是小程序開發(fā)者”連預(yù)覽都打不開。所以搭建真機(jī)測試池的時(shí)候一定要提前確認(rèn)哪些設(shè)備綁定了對應(yīng)的小程序開發(fā)者權(quán)限而且一般一個(gè)小程序賬號下能綁定的設(shè)備數(shù)量是有限的設(shè)備池規(guī)劃的時(shí)候要心里有數(shù)。4.2 真機(jī)調(diào)試網(wǎng)絡(luò)異常的排查真機(jī)調(diào)試小程序或者App時(shí)最讓人頭疼的報(bào)錯(cuò)是類似net::err_connection_reset這樣的網(wǎng)絡(luò)錯(cuò)誤。第一次遇到的時(shí)候我以為是代碼問題查了半天發(fā)現(xiàn)是網(wǎng)絡(luò)訪問不通。這類問題的排查思路一般有幾條。首先確認(rèn)測試環(huán)境是否在合法域名白名單里小程序的request、uploadFile、downloadFile這些接口都要求域名是HTTPS且在后臺配過白名單真機(jī)調(diào)試模式下可以勾選“不校驗(yàn)合法域名”來繞過但正式環(huán)境不行。其次是代理設(shè)置開發(fā)者工具或設(shè)備如果配了代理代理服務(wù)不穩(wěn)定就直接連接重置。再次是服務(wù)器端的TLS配置小程序?qū)LS版本有要求老舊的服務(wù)器配置會導(dǎo)致握手失敗。最后如果排除完上面所有原因都不通就檢查一下是不是測試環(huán)境的IP被設(shè)備訪問不了——比如服務(wù)綁定在宿主機(jī)回環(huán)地址上真機(jī)從局域網(wǎng)訪問自然不通。我的經(jīng)驗(yàn)是真機(jī)調(diào)試網(wǎng)絡(luò)問題要用排除法列表一項(xiàng)一項(xiàng)排查而不是憑感覺亂猜。把“網(wǎng)絡(luò)不通”當(dāng)做一個(gè)專項(xiàng)問題來對待整理一份排查清單能省下大量的排查時(shí)間。4.3 跨端框架的注意事項(xiàng)現(xiàn)在不少團(tuán)隊(duì)用uni-app這類跨端框架開發(fā)小程序和App好處是一套代碼多端復(fù)用但自動化測試的坑也隨之而來。第一個(gè)坑是“uniapp小程序不能真機(jī)調(diào)試”。uni-app編譯出來的小程序跟原生小程序的運(yùn)行環(huán)境還是有一層間接關(guān)系的經(jīng)常出現(xiàn)開發(fā)者工具里一切正常掃碼真機(jī)預(yù)覽卻白屏或者報(bào)錯(cuò)。解決方案多數(shù)是版本問題HBuilderX版本和小程序基礎(chǔ)庫版本不匹配導(dǎo)致的真機(jī)調(diào)試前先更新到對應(yīng)的版本組合。第二個(gè)坑是跨端條件下的環(huán)境差異。uni-app開發(fā)時(shí)很多API是條件編譯的微信小程序、App、H5各自走不同的代碼分支。自動化測試腳本如果只在開發(fā)者工具上驗(yàn)證過微信小程序分支那App端的邏輯可能是完全沒被覆蓋到的。所以我的建議是對于uni-app項(xiàng)目日常用模擬器跑開發(fā)者工具里的流程App端一定要在真機(jī)上走一遍完整的核心流程尤其是涉及平臺SDK的登錄、支付、分享。5. 穩(wěn)定性問題與效率優(yōu)化實(shí)錄5.1 模擬器環(huán)境的典型故障模擬器雖然方便但也不是絕對穩(wěn)定。最常見的故障是模擬器啟動失敗尤其是放在服務(wù)器上跑的。排查思路先看宿主機(jī)有沒有開啟虛擬化Intel平臺要開VT-xAMD平臺要開SVMBIOS里沒開的話模擬器直接起不來。其次是內(nèi)存分配給模擬器分配的內(nèi)存超過宿主機(jī)物理內(nèi)存表現(xiàn)為啟動到一半就閃退。再次是磁盤空間模擬器鏡像文件動輒幾十GB磁盤滿了之后模擬器會卡死或者無法創(chuàng)建快照。模擬器跑久了還有一個(gè)問題系統(tǒng)時(shí)間漂移。有些模擬器的系統(tǒng)時(shí)鐘會跟宿主機(jī)不同步導(dǎo)致應(yīng)用里的時(shí)間戳判斷出錯(cuò)。我們的處理方式是在每個(gè)task執(zhí)行前跑一次adb shell date跟宿主機(jī)時(shí)間對比超過一定閾值就自動校準(zhǔn)。另外模擬器的“假”還體現(xiàn)在某些系統(tǒng)行為不會觸發(fā)。比如App崩潰時(shí)真機(jī)會彈“微信停止運(yùn)行”的系統(tǒng)對話框模擬器可能直接靜默退出。如果用例依賴檢測崩潰彈窗來判斷是否異常那在模擬器上可能永遠(yuǎn)查不到問題。所以崩潰監(jiān)控這塊我的建議是不要只做UI層面的彈窗檢測還要做日志層面的異常檢測這樣在模擬器上也能發(fā)現(xiàn)崩潰。5.2 真機(jī)連接的常見故障真機(jī)連接問題比模擬器更多而且更隨機(jī)。先說說掉線問題。設(shè)備長期跑測試經(jīng)常出現(xiàn)adb devices里還能看到設(shè)備但執(zhí)行任何命令都報(bào)device not found或者device offline。處理方式比較粗暴先adb kill-server再adb start-server重啟ADB服務(wù)不行的話再執(zhí)行adb reconnect offline讓離線設(shè)備重連還不行就只能物理重插USB了。多臺設(shè)備同時(shí)跑的時(shí)候還有一個(gè)很容易被忽略的問題設(shè)備之間的狀態(tài)會互相干擾。比如兩臺設(shè)備同時(shí)跑同一個(gè)賬號相關(guān)的用例后登錄的設(shè)備會把先登錄的設(shè)備踢下線。所以我們的方案是每個(gè)測試任務(wù)執(zhí)行前先用設(shè)備池的占用機(jī)制鎖住設(shè)備任務(wù)結(jié)束后無條件清理應(yīng)用數(shù)據(jù)確保這臺設(shè)備回到初始狀態(tài)不給下一個(gè)任務(wù)留坑。還有一個(gè)真機(jī)特有的問題手機(jī)溫度過熱導(dǎo)致降頻。性能測試或者長時(shí)間跑用例手機(jī)發(fā)熱嚴(yán)重系統(tǒng)會自動降頻導(dǎo)致用例執(zhí)行時(shí)間變長甚至超時(shí)。我遇到過一次一臺設(shè)備跑了一晚上回歸早上過來一看后面一半的用例全是超時(shí)失敗但看日志又覺得一切都正常最后才發(fā)現(xiàn)是溫度問題。后來我們給長時(shí)間執(zhí)行的設(shè)備加了溫度監(jiān)控超過閾值就自動暫停任務(wù)讓設(shè)備冷卻。5.3 提升整體執(zhí)行效率的技巧協(xié)同方案的最終目標(biāo)是效率不是流程好看。我總結(jié)下來幾個(gè)提升效率的點(diǎn)非常有效。第一模擬器并行度可以大膽調(diào)。一臺16核64GB的宿主機(jī)開8個(gè)模擬器實(shí)例并行跑完全沒有問題。而真機(jī)并行受限于USB接口、供電和網(wǎng)絡(luò)帶寬并行度要保守很多。所以日?;貧w盡量讓模擬器多扛一些。第二用例執(zhí)行順序有講究。把用例按失敗概率排序容易失敗的用例放前面這樣執(zhí)行過程中如果出問題可以早發(fā)現(xiàn)、早停止避免無意義的繼續(xù)執(zhí)行浪費(fèi)時(shí)間。我們會在測試平臺里統(tǒng)計(jì)每個(gè)用例的歷史失敗率動態(tài)調(diào)整執(zhí)行優(yōu)先級。第三模擬器復(fù)用而不是重復(fù)創(chuàng)建。創(chuàng)建模擬器實(shí)例耗時(shí)很長如果每輪測試都從鏡像恢復(fù)時(shí)間成本太高。我的做法是維護(hù)一個(gè)“熱池”提前啟動若干臺模擬器任務(wù)結(jié)束不銷毀只是清理數(shù)據(jù)下一個(gè)任務(wù)進(jìn)來直接復(fù)用。第四數(shù)據(jù)準(zhǔn)備服務(wù)化。測試用例最怕在準(zhǔn)備數(shù)據(jù)上浪費(fèi)時(shí)間登錄、注冊、造數(shù)這些操作應(yīng)該做成一個(gè)公共的服務(wù)用例直接調(diào)用而不是每個(gè)用例都自己走一遍流程。數(shù)據(jù)的準(zhǔn)備過程放到調(diào)度器的前置階段用例只負(fù)責(zé)驗(yàn)證結(jié)果。5.4 常見問題速查表把我在實(shí)際運(yùn)維中遇到的典型問題匯總成了一張表方便排查時(shí)對照現(xiàn)象可能原因排查思路模擬器啟動失敗虛擬化未開啟、內(nèi)存不足檢查BIOS的VT/SVM、宿主機(jī)內(nèi)存占用adb連接正常但設(shè)備offlineADB服務(wù)異常、設(shè)備USB調(diào)試權(quán)限失效重啟ADB服務(wù)、重新授權(quán)USB調(diào)試用例在模擬器通過、真機(jī)失敗設(shè)備能力差異、彈窗干擾先看設(shè)備類型分支是否匹配、彈窗庫是否覆蓋真機(jī)訪問localhost失敗缺少adb reverse映射執(zhí)行adb reverse并加入初始化腳本小程序真機(jī)預(yù)覽白屏編譯版本和基礎(chǔ)庫不匹配更新HBuilderX和微信開發(fā)者工具版本地圖API無法調(diào)試工具不支持該API改用真機(jī)并打標(biāo)跳過工具環(huán)境長時(shí)間執(zhí)行后用例變慢設(shè)備發(fā)熱降頻監(jiān)控溫度、暫停任務(wù)冷卻多條用例共用一個(gè)賬號互踢狀態(tài)隔離不完整任務(wù)執(zhí)行前清理數(shù)據(jù)、鎖設(shè)備排查問題時(shí)一定要記住一個(gè)原則先看環(huán)境再看代碼。自動化測試?yán)锎罅康膯栴}其實(shí)都是環(huán)境問題而不是用例問題。不要一上來就懷疑代碼寫錯(cuò)了先把設(shè)備狀態(tài)、網(wǎng)絡(luò)情況、版本匹配這些因素排除掉往往能少走很多彎路。我在實(shí)際項(xiàng)目里把這套方案跑起來之后最大的感受是設(shè)備不再是瓶頸執(zhí)行速度和對環(huán)境的信任度終于可以兼得?;叵肫饋磉@套方案的落地過程中最花時(shí)間的不是寫用例也不是搭設(shè)備池而是把“哪些用例該跑模擬器、哪些必須跑真機(jī)”這個(gè)決策邏輯和團(tuán)隊(duì)達(dá)成一致。只要規(guī)則定清楚了剩下的事情無非就是執(zhí)行和完善。如果你正在糾結(jié)真機(jī)和模擬器的選擇不妨試試先把它們分開看再合起來用可能一下就通透了。