跳至內容

Appium 驅動程式簡介

正如 主要概觀 所說明的,「驅動程式」基本上是 Appium 對「我們如何支援多個不相關平台的自動化?」這個問題的解答。在本文檔中,我們將更深入地探討驅動程式的運作方式。除非您計畫撰寫自己的驅動程式或為現有驅動程式做出貢獻(我們希望您能這麼做!),否則驅動程式運作方式的具體細節可能對您來說並不重要。

了解驅動程式運作方式的更多細節的主要好處在於,當您在測試中遇到問題時,了解典型的複雜性或典型的驅動程式架構將有助於您的除錯程序。

介面實作

在最基本的層面上,驅動程式只是延伸 Appium 中包含的一個特殊類別(稱為 BaseDriver)的 Node.js 類別。您可以使用這些非常簡單的程式碼行來建立一個非常接近「運作中」的驅動程式

import BaseDriver from '@appium/base-driver'

class MyNewDriver extends BaseDriver {
}

這個空的驅動程式不會執行任何操作,但您可以將它包裝在一個 Node.js 模組中,將一些與 Appium 相關的欄位新增到模組的清單(package.json),然後使用 appium driver install 來安裝它。

因此,從技術角度來看,Appium 驅動程式只是一些繼承自其他 Appium 程式碼的程式碼。就是這樣!現在,繼承自 BaseDriver 實際上為我們提供了很多好處,因為 BaseDriver 本質上是整個 WebDriver 協定的封裝。因此,驅動程式需要執行的所有有用操作就是以與其 WebDriver 協定等效的名稱來實作 Node.js 方法。

因此,假設我想使用這個空的驅動程式執行一些操作;首先我必須決定要實作哪個 WebDriver 指令。對於我們的範例,我們採用 導航至 WebDriver 指令。暫時先不考慮我希望驅動程式在執行此指令時執行什麼操作。為了告訴 Appium 驅動程式可以處理此指令,我們只需在驅動程式類別中定義一個像這樣的函式:1

async setUrl(url) {
    // do whatever we want here
}

就是這樣!我們如何實際實作指令完全取決於我們,並取決於我們想要支援的平台。以下是針對不同平台的此指令的一些不同的實作範例

  • 瀏覽器:執行一些 JavaScript 來設定 window.location.href
  • iOS 應用程式:使用深度連結啟動應用程式
  • Android 應用程式:使用深度連結啟動應用程式
  • React 應用程式:載入特定路由
  • Unity:前往命名場景

因此,您會看到,在不同平台上,驅動程式實作相同 WebDriver 命令的方式可能會有很大的差異。2不過,它們表達自己可以處理通訊協定命令的方式相同

我們會深入探討這些細節(順帶一提,您不需要記住這些細節),因為強調 Appium 驅動程式本身並非特定事物非常重要,它只不過是可以處理 WebDriver 通訊協定命令的 JS 程式碼。接下來的步驟取決於您,也就是驅動程式作者!

自動化對應

但是,通常驅動程式作者想要做的是為特定平台提供自動化行為,這些行為在語義上與瀏覽器的 WebDriver 規格實作非常相似。當您想要尋找元素時,您應該取得 UI 元素的參考。當您想要按一下或輕觸該元素時,產生的行為應該與人按一下或輕觸元素時相同。以此類推。

因此,驅動程式作者面臨的真正挑戰並非如何使用 WebDriver 通訊協定(因為 BaseDriver 會為您封裝所有這些內容),而是如何在目標平台上執行實際的自動化。每個驅動程式在此處都依賴其自己的底層技術組。如 概觀 中所述,iOS 驅動程式使用 Apple 的技術,稱為 XCUITest。這些底層自動化技術通常有自己的專有或特殊 API。撰寫驅動程式變成將 WebDriver 通訊協定對應到此底層 API(或有時是不同的底層 API 組)的任務(例如,UiAutomator2 驅動程式不僅依賴 Google 的 UiAutomator2 技術,還依賴只能透過 ADB 使用的功能,以及只能透過 Android SDK 在輔助應用程式中使用的功能)。將所有這些功能結合到單一、可用且 WebDriver 的介面中,就是驅動程式開發令人難以置信的有用(但難度也令人難以置信)的技術!

多層架構

在實務上,這通常會導致相當複雜的架構。我們再次以 iOS 為例。XCUITest 架構(Appium 驅動程式使用的架構)預期呼叫它的程式碼是用 Objective-C 或 Swift 編寫的。此外,XCUITest 程式碼只能在 Xcode 觸發的特殊模式中執行(直接或間接地,Xcode 命令列工具)。換句話說,沒有直接的方法可以從 Node.js 函式實作(例如上述的 setUrl())轉換為 XCUITest API 呼叫。

XCUITest 驅動程式作者所做的是將驅動程式分成兩部分:一部分以 Node.js 編寫(整合至 Appium 中,最初處理 WebDriver 指令),另一部分以 Objective-C 編寫(實際在 iOS 裝置上執行,並呼叫 XCUITest API)。這使得與 XCUITest 的介面成為可能,但也引入了兩部分之間協調的新問題。

驅動程式作者可以選擇許多不同的策略來建模 Node.js 端與 Objective-C 端之間的通訊,但最後決定使用...WebDriver 協定!沒錯,XCUITest 驅動程式的 Objective-C 端本身就是一個 WebDriver 實作,稱為 WebDriverAgent3

  • Appium XCUITest 驅動程式會為您建置並管理 WebDriverAgent,這可能會很麻煩,而且需要使用 Xcode。
  • XCUITest 驅動程式所做的遠遠超過 WebDriverAgent 所能做到的,例如使用模擬器或裝置、安裝應用程式等。

這個故事的寓意是,由於我們試圖解決問題的性質,驅動程式架構可能會變得相當複雜且多層。這也表示,如果您在特定測試中遇到問題,有時可能會很難找出這個技術鏈中的哪個環節出錯了。回到 XCUITest 的世界,我們同時擁有以下這類技術

  • 您的測試程式碼(以其程式語言撰寫) - 由您擁有
  • Appium 應用程式程式庫 - 由 Appium 擁有
  • Selenium 應用程式程式庫 - 由 Selenium 擁有
  • 網路(區域網路或網際網路)
  • Appium 伺服器 - 由 Appium 擁有
  • Appium XCUITest 驅動程式 - 由 Appium 擁有
  • WebDriverAgent - 由 Appium 擁有
  • Xcode - 由 Apple 擁有
  • XCUITest - 由 Apple 擁有
  • iOS 本身 - 由 Apple 擁有
  • macOS(Xcode 和 iOS 模擬器執行的環境) - 由 Apple 擁有

這是一個相當深的堆疊!

代理模式

還有另一個重要的驅動程式架構面向需要了解。XCUITest 驅動程式再次可以作為範例。回想一下,我們剛剛討論過 XCUITest 驅動程式的兩個「半部」如何都使用 WebDriver 協定---Node.js 半部直接點擊 Appium 的 WebDriver 伺服器,而 Objective-c 半部(WebDriverAgent)則是它自己的 WebDriver 實作。

這讓 Appium 有機會在某些情況下採取捷徑。讓我們想像 XCUITest 驅動程式需要實作 Click Element 指令。此實作的內部程式碼看起來會像取得適當的參數並建構一個 HTTP 要求傳送至 WebDriverAgent 伺服器。在這種情況下,我們基本上只是重建用戶端對 Appium 伺服器的原始呼叫!4 所以甚至沒有必要撰寫實作 Click Element 指令的函式。相反地,XCUITest 驅動程式可以讓 Appium 知道此指令應直接代理到其他 WebDriver 伺服器。

如果您不熟悉「代理」的概念,在這種情況下,這表示 XCUITest 驅動程式將完全不參與處理指令。相反地,它只會在通訊協定層級重新封裝並轉送至 WebDriverAgent,而 WebDriverAgent 的回應也會直接傳回用戶端,而不會讓任何 XCUITest 驅動程式程式碼看到或修改它。

此架構模式為選擇在各處處理 WebDriver 通訊協定的驅動程式作者提供了一個不錯的紅利,而不是建構客製化通訊協定。這也表示 Appium 可以非常輕鬆地為任何其他現有的 WebDriver 實作建立包裝器驅動程式。例如,如果您查看 Appium Safari 驅動程式 程式碼,您會看到它基本上沒有實作任何標準指令,因為所有這些指令都直接代理到底層的 SafariDriver 程序。

了解此代理業務有時會在幕後發生非常重要,因為如果您曾經深入探討某些開放原始碼驅動程式程式碼,試圖找出指令在哪裡實作,您可能會驚訝地發現 Node.js 驅動程式程式碼本身根本沒有實作!在這種情況下,您需要找出指令代理到的位置,以便您可以在那裡尋找適當的實作。

好的,這對於此非常詳細的驅動程式簡介來說已經足夠了!


  1. 您可能會注意到,setUrl 看起來與 導航至 完全不同,那麼我們如何知道使用它而不是其他隨機字串呢?嗯,Appium 的 WebDriver 協定至方法名稱對應關係定義在 @appium/base-driver 套件中的特殊檔案 routes.js 中。因此,如果您正在撰寫驅動程式,這將是您找出要使用哪些方法名稱和預期哪些參數的地方。或者,您可以查看任何主要 Appium 驅動程式的原始碼! 

  2. 當然,我們希望語意盡可能相似,但在 iOS 的世界中,例如,透過深度連結 (具有特殊應用程式特定架構的 URL) 啟動應用程式,與導航至網路 URL 已經非常接近了。 

  3. 因此,理論上,您可以將您的 WebDriver 客戶端直接指向 WebDriverAgent,並完全繞過 Appium。然而,由於一些原因,這通常並不方便: 

  4. 它並非完全相同的呼叫,因為 Appium 伺服器和 WebDriverAgent 伺服器會產生不同的工作階段 ID,但這些差異將會以透明的方式處理。