iOS自動化測試驅動工具探索

本文主要介紹了字節 iOS 自動化測試驅動工具的探索過程及實現原理

作者:字節跳動終端技術——陳友輝

一、背景

隨着業務的擴張,單個 App 的功能越來越多,工程複雜度越來越高,每天MR可達上百次,代碼變更可達上千處,航母級的 App 在這一點上更爲嚴重。如何在頻繁的代碼變更中保障App質量,成了各個業務的痛點。靠傳統的人工測試已無法滿足各業務的需求,我們需要將更多的測試場景自動化。

自動化測試需要將人工交互行爲變成自動化的原子操作。比如應用安裝卸載、屏幕點拖拽及縮放、實體按鍵點擊、設備信息獲取、應用啓停等等。這就需要一款工具來驅動 iOS 設備完成以上操作。這篇文章主要介紹字節 iOS 自動化測試驅動工具 bdc 的探索過程及實現原理。

二、功能介紹

在介紹 bdc 的探索過程及實現原理之前,先介紹一下 bdc 的能力:

三、探索歷程

早期方案

在字節開始大規模建設自動化建設時,Android 已經有較爲完善的解決方案,包括其生態自帶的驅動工具 adb 及開源的雲真機管理平臺 STF。但 iOS 在這方面相對滯後,主要是 iOS 缺少一款類似 adb 功能齊全且穩定的驅動工具。

早期 iOS 採用了 Facebook 開源的方案,Facebook 在驅動工具方面先後開源了 wda 與 idb,wda 支持 UI 交互操作,idb 支持應用管理,這在一定程度上滿足了我們的需求,基於這套方案,搭建了第一個版本的自動化測試機架。

早期的機架也很簡單,機器的規模也不大

經過一段時間的實踐,我們遇到了以下幾個問題

  • wda 部分接口執行耗時較長,效率低下,無法滿足高頻率調度的需求
  • idb 很多命令只支持模擬器,對真機不夠友好,無法滿足我們的功能擴展
  • 命令執行失敗率高,工具穩定性差,且出問題後難以排查
  • 整套流程強依賴 Xcode 環境,規模化、自動化部署成本高,無法應對上千臺手機的部署

工具改良

UI 交互改造

爲了解決上述問題,我們結合 wda 的實現思路,實現了一個更高效穩定的 XCTest 工具。我們對 XCTest 相關的接口進行了review,並找到了XCTest 實現跨進程調用最底層的接口。通過這些接口,可以直接調用 testmanagerd 進程。隨後基於這些底層接口封裝了一套新的接口,可以實現屏幕的點拖拽、實體按鍵點擊、文本輸入等操作。相比 wda,在執行速度和穩定性上獲得大幅提升。

testmanagerd 進程是一個開發者守護進程,在 iOS 設備開啓開發者模式後,testmanagerd 進程的鏡像會被掛載到 iOS 設備系統的 Developer 目錄,並被 launchd 進程啓動。XCTest 使用蘋果自帶的 XPC 機制與 testmanagerd 進行通信,利用 NSXPCConnection,只需知道服務的 id 即可建立通信,testmanagerd 服務的 id 可在其鏡像文件裏找到。鏡像的路徑位於:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport

解壓鏡像後,可在 Library/LaunchDeamons 目錄下找到名爲 com.apple.testmanagerd 的 plist 文件,打開後可以看到其 id 爲 com.apple.testmanagerd(iOS14 後 id 有所變動)

設備交互工具

搞定 UI 交互後,接下來就需要找到能完美支持設備管理、應用管理、沙盒文件管理的方案。一開始我們也是想基於 idb 進行優化,但隨後發現 idb 內部使用了大量的 api,這些 api 絕大部分缺少文檔,優化成本較高,且這些私有 api 會隨着 Xcode 版本的變動而更新,維護會很麻煩。所以放棄了 idb 轉而尋找其他替代方案。

這時我們發現了另一個開源實現 libimobiledevice,libimobiledevice 支持通過 USB 的方式與 iOS 設備進行通信,且支持應用安裝卸載、設備信息獲取、沙盒文件操作等功能。libimobiledevice 在使用體驗上,操作簡單,功能穩定。但缺點是功能有限,不能完全符合我們的訴求,接下來我們對 libimobiledevice 的實現原理進行了探究。

基於 USB 與 iOS 設備通信

蘋果自身有一些 Mac App 需要通過 USB 跟 iOS 設備進行通信,比如 iTunes、XCode 及其套件等等。雙端通信需要基於一定的協議,通過USB通信需要使用USB協議,但USB協議具有一定的侷限性,直接使用成本較高。所以蘋果在USB協議的基礎上支持了TCP通信的能力,以此減小使用成本。

蘋果通過 usbmuxd 來提供基於 USB 實現 TCP 通信的能力。usbmuxd是一個守護進程,它在 USB協議上實現了多路 TCP 連接,可以讓應用層無感知的基於 USB 通道進行 TCP 通信。

macOS 上的 usbmuxd 配置文件位於/Library/Apple/System/Library/LaunchDaemons,打開後如下:

usbmuxd 的配置文件記錄了加載屬性、服務名稱、可執行文件路徑、socket 屬性等信息。從上面的配置文件可以看到,usbmuxd 創建了一個 Unix 域的 socket。這個 socket 主要用於跟上層應用建立連接,實現跨進程通信。基於 usbmuxd 進行網絡通信的流程如下:

發現iOS設備系統服務並完成調用

從上述 usbmuxd 的通信流程可知,想要跟iOS設備中的服務進行通信,只需要知道具體服務的端口即可。那麼服務端口該如何獲取?一般有兩種方式,一種是直接hardcode,另一種是動態獲取。iOS設備內部的服務衆多,如果全部 hardcode,其維護成本較高,且安全性及穩定性較低。如果是動態獲取,那麼獲取的方式將成爲另一個問題,因爲動態獲取本身也需要通信。

蘋果則採用了兩者結合的方式。蘋果在iOS系統內部增加了一個 lockdownd 的守護進程,這個守護進程以 root 特權運行,具有訪問 iOS 系統信息的能力,且運行在固定的端口。lockdownd 的配置文件如下,其路徑位於 iOS 系統的/System/Library/LaunchDaemons/目錄。

由此可以看出,lockdownd 分別支持 Unix 域的 socket 與非 Unix 域的 socket,對於非 Unix 域的socket,其監聽的端口固定在62078

當 PC 端想要跟 iOS 設備中的某個服務進行通信時,先通過 lockdownd 查找對應服務的端口,然後再跟對應的服務建立 socket 連接,其流程如下:

libimobiledevice 就是利用以上流程對 iOS 設備中的服務進行了調用,比如在操作沙盒文件時,就是調用了 iOS 設備中的 afc、house_arrest 服務。

服務類型

搞清楚了調用流程,那麼 iOS 設備中都有哪些服務呢?iOS 系統服務主要分爲兩類:一類是debug相關服務,打開開發者選項後才具備相應服務。另一類是非 debug 服務,默認都具備。

debug 相關服務的配置文件及可執行文件都被打包放在了 Xcode 中,位於/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/目錄,當 iOS 設備連接 Xcode 時,Xcode 會自動將此目錄下對應的DeveloperDiskImage掛載到 iOS 系統中。其包含的服務如下:

上述提到的 testmanagerd 就位於其中。

非debug服務位於iOS系統的/System/Library/LaunchDaemons目錄。通過越獄後,我們可以查看/System/Library/LaunchDaemons目錄下的內容,以下是部分服務的截圖,可以看到,服務非常之多。

能力擴展

搞清楚 libimobiledevice 實現原理後,基於這個思路,我們又自行探索了其他一些服務的能力,這其中就包括 Xcode 相關的服務。Xcode 爲開發者提供了專業、穩定的工具集,而 Xcode 的工具集也是利用以上機制與 iOS 設備進行通信。基於Xcode 的能力,我們實現了 Trace 採集、設備應用管理等功能。

支持 Linux

工具的問題解決了,接下來就是部署的問題。我們需要面對上千臺 iOS 設備接入及每天上萬次的服務調度,且設備之間的環境需要相互隔離,互不干擾。所以在設備及工具的部署上需要簡單、高效。比較好的解決方案是採用 docker 部署,每臺設備及其對應的驅動工具都用 docker 分離開,這樣能做到環境隔離,且部署簡單。但 Mac 對 docker 的支持並不那麼友好,且我們的工具本身依賴 Xcode 環境。

既然 Mac 對 docker 的支持不友好,那我們是否能擺脫對 Mac 的依賴,將設備及工具部署在 Linux 上。順着這個思路,我們開始了對工具的第二次改造。

支持 XCTest 啓動

工具對 Mac 的依賴主要來源於 XCTest 工具的啓動,類似於 wda。我們將 XCTest 的接口封裝在了一個普通的 App 裏,然後在這個普通的 App 裏搭建一個 websocket server,這樣就可以通過網絡與這個 App 通信,實現 XCTest API 的調用。但經過嘗試後,發現普通的 App 調用 XCTest API 並不會產生預期的效果。所以還需要一些特殊的操作才能使普通App具備調用 XCTest API 的能力。

我們從正常的 XCTest-Runner 入手探索其啓動流程。以home鍵的點擊方法爲切入點,通過 LLDB 追蹤testmanagerd 進程中接口的調用流程,發現瞭如下關鍵的接口:

_IDE_authorizeTestSessionWithProcessID:

分析後發現,testmanagerd 會維護一個進程白名單,只有將 App 的進程 ID 加到這個白名單裏,這個App 才具備調用 XCTest API 的能力。而上述註冊白名單的接口則可以通過前面提到的 lockdownd 的方式調用。基於此,我們實現了擺脫對 Mac 環境的依賴,在 Linux 實現了 docker 化部署。

四、運行效果

bdc 工具上線已有一年多。目前支撐了公司自動化測試平臺上千臺 iOS 設備每日上萬次的設備調度及測試任務執行。自動化測試平臺涵蓋了穩定性、UI、性能、單元測試等多項測試能力,服務公司上百個業務。

設備機架從原有的簡陋設備已升級爲全球機房,服務於全球業務。


🔥 火山引擎 APMPlus 應用性能監控是火山引擎應用開發套件 MARS 下的性能監控產品。我們通過先進的數據採集與監控技術,爲企業提供全鏈路的應用性能監控服務,助力企業提升異常問題排查與解決的效率。目前我們面向中小企業特別推出「APMPlus 應用性能監控企業助力行動」,爲中小企業提供應用性能監控免費資源包。現在申請,有機會獲得60天免費性能監控服務,最高可享6000萬條事件量。

👉 點擊這裏,立即申請

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章