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