コンテンツへスキップ

Appium ドライバー入門

概要 で明らかなように、「ドライバー」は基本的にAppiumが「複数の無関係なプラットフォームの自動化をどのようにサポートするか」という問題に対する回答です。このドキュメントでは、ドライバーの動作についてもう少し詳しく説明します。ドライバーの具体的な動作の詳細については、独自のドライバーを作成したり、既存のドライバーに貢献したりする予定がない限り、あまり重要ではないでしょう(私たちはあなたがそうすることを願っています!)。

ドライバーの動作についてもう少し理解することで得られる主な利点は、典型的な複雑さや典型的なドライバーアーキテクチャを認識することで、テストで問題が発生した場合のデバッグプロセスが向上することです。

インターフェースの実装

最も基本的なレベルでは、ドライバーは単にAppiumに含まれる特別なクラス(BaseDriver)を拡張するNode.jsクラスです。これらの非常に単純なコード行で、「動作する」ドライバーに非常に近いものを作成できます。

import BaseDriver from '@appium/base-driver'

class MyNewDriver extends BaseDriver {
}

この空のドライバーは何も実行しませんが、Node.jsモジュールにパッケージ化し、モジュールのマニフェスト(package.json)にいくつかのAppium関連のフィールドを追加してから、appium driver installを使用してインストールできます。

したがって、技術的な観点から見ると、Appiumドライバーは、他のAppiumコードを継承する小さなコードです。それだけです!さて、BaseDriverを継承すると、実際には多くのものが得られます。なぜなら、BaseDriverは基本的にWebDriverプロトコル全体のカプセル化だからです。したがって、ドライバーが何か有用なことを行うために必要なことは、WebDriverプロトコルと同等の名前を持つNode.jsメソッドを実装することです。

そこで、この空のドライバーで何かを行いたいとします。最初に、実装するWebDriverコマンドを決定する必要があります。例として、Navigate To WebDriverコマンドを取り上げます。このコマンドが実行されたときにドライバーに何をさせたいかは、今は脇に置いておきます。ドライバーがこのコマンドを処理できることをAppiumに伝えるには、ドライバークラスに次のようなメソッドを定義するだけです。1

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

それだけです!コマンドを実際にどのように実装するかは完全に私たち次第であり、サポートしたいプラットフォームによって異なります。さまざまなプラットフォームにおけるこのコマンドの異なる実装例を次に示します。

  • ブラウザ:window.location.hrefを設定するJavaScriptを実行します。
  • iOSアプリ:ディープリンクを使用してアプリを起動します。
  • Androidアプリ:ディープリンクを使用してアプリを起動します。
  • Reactアプリ:特定のルートを読み込みます。
  • Unity:名前付きシーンに移動します。

ご覧のとおり、ドライバーがプラットフォーム間で同じWebDriverコマンドを実装する方法には多くの違いがあります。2 しかし、同じなのは、プロトコルコマンドを処理できることをどのように表現するかです。

これほど詳細に説明しているのは(覚えておく必要はありませんが)、Appiumドライバーは本質的に特定のものではなく、WebDriverプロトコルコマンドを処理できるJSコードの一片であることを強調することが重要だからです。そこからどこに行くかは、ドライバーの作成者であるあなた次第です!

自動化マッピング

しかし、通常、ドライバー作成者が行いたいのは、特定のプラットフォームの自動化動作を、ブラウザのWebDriver仕様の実装と意味的に非常に似せることです。要素を見つけたい場合は、UI要素への参照を取得する必要があります。その要素をクリックまたはタップしたい場合、結果は、人がその要素をクリックまたはタップした場合と同じになります。など。

そのため、ドライバー作成者にとっての本当の課題は、WebDriverプロトコルをどのように操作するかではありません(BaseDriverがすべてをカプセル化するため)、ターゲットプラットフォームで実際の自動化をどのように行うかです。すべてのドライバーは、ここで独自の基盤となるテクノロジーセットに依存しています。概要 で説明したように、iOSドライバーはXCUITestと呼ばれるAppleのテクノロジーを使用しています。これらの基盤となる自動化テクノロジーは、通常、独自の独自の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ドライバーの作成者は、代わりにドライバーを2つの部分に分割しました。1つはNode.jsで記述された部分(Appiumに組み込まれており、最初にWebDriverコマンドを処理する部分)、もう1つはObjective-Cで記述された部分(実際にiOSデバイスで実行され、XCUITest API呼び出しを行う部分)です。これにより、XCUITestとのインターフェースが可能になりますが、2つの部分間の調整という新しい問題が発生します。

ドライバーの作成者は、Node.js側とObjective-C側間の通信をモデル化するために、さまざまな戦略を選択できましたが、最終的には…WebDriverプロトコルを使用することにしました!そうです、XCUITestドライバーのObjective-C側は、WebDriverAgentと呼ばれるWebDriverの実装でもあります。3

  • 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が所有

かなり深いスタックです!

プロキシモード

理解すべきドライバーのもう1つの重要なアーキテクチャ上の側面があります。それは、XCUITestドライバーで再び例示できます。XCUITestドライバーの2つの「半分」が両方ともWebDriverプロトコルを話すことを説明しました。Node.jsの半分はAppiumのWebDriverサーバーに直接接続し、Objective-cの半分(WebDriverAgent)は独自のWebDriver実装です。

これにより、Appiumが特定のケースでショートカットを使用できる可能性が開けます。XCUITestドライバがClick Elementコマンドを実装する必要があるとしましょう。この実装の内部コードは、適切なパラメータを取得してWebDriverAgentサーバーへのHTTPリクエストを構築することに似ています。この場合、基本的にクライアントによるAppiumサーバーへの元の呼び出しを再構築しているだけです!4 したがって、Click Elementコマンドを実装する関数を記述する必要は実際にはありません。代わりに、XCUITestドライバは、このコマンドを他のWebDriverサーバーに直接プロキシする必要があることをAppiumに知らせるだけです。

「プロキシ」の概念に慣れていない場合、このケースでは、XCUITestドライバがコマンドの処理にまったく関与しないことを意味します。代わりに、プロトコルレベルでWebDriverAgentに再パッケージ化して転送され、WebDriverAgentの応答も同様に、XCUITestドライバコードがそれを参照したり変更したりすることなく、クライアントに直接返されます。

このアーキテクチャパターンは、カスタムプロトコルを構築するのではなく、WebDriverプロトコルをあらゆる場面で扱うことを選択するドライバ作成者にとって大きなメリットをもたらします。また、Appiumは、他の既存のWebDriver実装のラッパードライバを非常に簡単に作成できることも意味します。Appium Safariドライバのコードを見ると、標準コマンドはほとんど実装されておらず、すべてが基礎となるSafariDriverプロセスに直接プロキシされていることがわかります。

このプロキシ処理は、時としてバックグラウンドで行われていることを理解することが重要です。なぜなら、コマンドがどこで実装されているかを理解しようとしてオープンソースドライバコードを調べると、Node.jsドライバコード自体に実装がないことに驚くかもしれません!その場合、コマンドがどこにプロキシされているかを特定して、適切な実装を探さなければなりません。

さて、ドライバに関するこの非常に詳細な紹介はここまでです!


  1. setUrlNavigate Toとはまったく異なるように見えることに気付くかもしれません。では、他のランダムな文字列ではなく、それをどのようにして使用するとわかったのでしょうか?AppiumのWebDriverプロトコルからメソッド名へのマッピングは、@appium/base-driverパッケージ内の特別なファイルroutes.jsで定義されています。そのため、ドライバを作成する場合は、ここで使用するメソッド名と期待されるパラメータを調べることができます。または、主要なAppiumドライバのソースコードを参照することもできます!

  2. もちろん、セマンティクスは可能な限り類似するようにしたいと考えていますが、たとえばiOSの世界では、ディープリンク(アプリ固有の特別なスキームを持つURL)経由でアプリを起動することが、Web URLへのナビゲーションに最も近い方法です。

  3. 理論的には、WebDriverクライアントをWebDriverAgentに直接ポイントして、Appiumを完全にバイパスすることもできます。しかし、いくつかの理由から、これは通常は便利ではありません。

  4. AppiumサーバーとWebDriverAgentサーバーは異なるセッションIDを生成するため、まったく同じ呼び出しではありませんが、これらの違いは透過的に処理されます。