ローソンデジタルイノベーション テックブログ

ローソンデジタルイノベーション開発チームのテックブログです

AppiumでXPathを使用して目的の要素を取得する

f:id:wave1008:20210208230858p:plain

ども。LDI品質管理部の仙波です。
スマホアプリの自動テストを担当しています。

最近はDysonの空気清浄ファンヒーターがお気に入りですが、電気料金の請求書を見てちょっと焦ってます。


前回は Androidアプリの画面を操作・検証する基本的なコードを紹介しました。
今回は任意の要素を取得するために便利なテクニックを紹介します。


問題のコンテキスト

Appiumで要素を取得する場合、Androidならresource-idやcontent-desc、あるいはtextなどの属性を指定して要素を取得することができます。

指定した属性で要素がユニークになる場合は問題ありませんが、属性の値が同じ要素が複数存在する場合はこの方法ではうまくいきません。

たとえば、ネットワークとインターネット画面では、使用しているWi-Fiやモバイルネットワークを確認することができます。

Wi-Fiやモバイルネットワークに設定されている値を取得するテストコードを書くことを考えてみましょう。




画面情報のXML

f:id:wave1008:20210208233624p:plain


この画面のXMLを確認すると、これらの要素は共に resoure-id"=android:id/summary" であることがわかります。

このresource-idを指定した場合、取得できるのは現在画面に表示されている要素のうち、最初にマッチした要素になるので text="AndroidWifi"の要素(Wi-Fiの設定値)を取得することになります。

モバイルネットワークの設定値を取得するにはこの方法は適切ではありません。

ユニークな要素を取得し、そこからの相対関係で目的の要素を取得する

属性指定でピンポイントで要素を取得できない場合、その周辺の要素で何かユニークな属性も持つ要素があれば、そこからの相対位置で目的の要素を取得することができます。


f:id:wave1008:20210209001111p:plain
この例だと text="モバイル ネットワーク" である要素がユニークなのでこれを利用します。

目的の要素はこの要素の次に出現する要素なので、XPathを使用して

//*[@text='モバイル ネットワーク']/following::*[1]

で取得することができます。

テストコードの例

上記のアイデアを実装したテストコードを示します。
前回までに作成したHelloAppiumクラスに以下のコードを追加します。

  @Test
  fun getRelative() {

      val d = getAppiumDriver()

      // ①「ネットワークとインターネット」を取得する
      val e = d.findElementByXPath("//*[@text='ネットワークとインターネット']")

      // ②取得した要素をクリックする
      e.click()
      Thread.sleep(500)

      // ③モバイルネットワークの設定値が"T-Mobile"であることを検証する
      val mobileNetwork = d.findElementByXPath("//*[@text='モバイル ネットワーク']/following::*[1]")
      assertThat(mobileNetwork.text).isEqualTo("T-Mobile")

  }


これを右クリックしてDebug実行するとテストが実行され、期待値と一致します。

f:id:wave1008:20210209231219p:plain

AppiumにおけるXPathの使用について

Appium Desktopを使用しアプリの画面をキャプチャして要素を選択すると
その要素にアクセスできるxpathが表示されますが、これは使用してはいけません。


f:id:wave1008:20210209010921p:plain


下部に記載された注意書きには

XPathロケーターの使用は脆弱なテストになるため推奨されていません。
ユニークなアクセシビリティ用のロケーターを付与するように
あなたの開発チームに依頼して下さい。

とあります。

だったら表示しなければいいじゃないかと思うわけですが、いろいろやってみるとこの注意書きの内容そのものも妥当ではないことに気づきます。


まず、アプリ開発者が全ての画面要素にユニークな属性をあらかじめ付与することは実務上困難です。

画面の要素が全て固定ならば問題ないかもしれませんが、スクロール等による表示内容が可変の画面において、各行にユニークな属性を埋め込むことは大変な作業であり、場合によってはテストコード作成者にとっても意味がありません。

また、アプリ開発者に依存するため、テストコード作成者で自己解決できる余地がありません。


次に、この注意書きはXPathの全てが推奨されないように読めますが、推奨されないのはルート要素からの出現順序や構造に依存した書き方をしている場合です。

そのような書き方のXPathはちょっとスクロール位置がズレたり、イレギュラーな要素が表示されたりすると目的の要素に正しくアクセスできなくなるので使うべきではありません。

一方、この記事で取り上げたように要素をユニークな属性でフィルター処理して取得したり、そこからの相対関係で目的の要素を取得したりするような方法は、比較的安定して要素にアクセスすることができます。

XPathは非常に強力な要素検索を実装しているので、状況次第ではこれを利用しない手はありません。

他方で、XPathについてはパフォーマンス的に不利なので使うべきではないという議論があり、注意が必要です。そうは言ってもXPath以外のロケーターでは解決できないこともあります。

XPathにおけるAppium Serverやモバイル端末におけるオーバーヘッド(主にシリアライズ処理)は確かに存在しますが、そもそもUIの自動テストは全体的に重い処理なので、許容可能な範囲であれば待てば良いという話なのかもしれません。


まとめ

  • AppiumにおいてXPathは使うべきではないとされていますが、脆弱なテストコードになる書き方をしなければ、あまり問題はありません。
  • XPathは非常に強力な要素検索ができるので、テストを自動化できる範囲が広がります。パフォーマンス上の特性を念頭に、ケースバイケースで活用しましょう。


次回はAppiumでXPathを使用するとどれくらい遅いのかを深掘りしてみます。