中年エンジニアの開発と生活の日々

中年エンジニアがソフトウェア開発や日々の生活で得た知見の備忘録

iOSとAndroidのAppiumスクリプトを共通化する (その2)

前回、iOSAndroid の Appium スクリプトを共通化するお話を書きました。前回、分量の関係で書けませんでしたが、iOSAndroidスクリプトを共通化する場合、避けて通れない話があります。

iOSAndroid で find_element() の挙動が違うお話

iOSAndroid で Appium のスクリプトを共通化するに当たって困るパターンのうち、「Appium の API は同じなのにテストフレームワークの違いによって、挙動が異なる」パターンに分類されます。

accessibility id を 使用して find_element() するさいに、androidiOS で以下のような違いがあります。

  • iOS では画面外の UI コントロールが find_element() で見つけることができる
    • このとき、UI コントロールの displayed? の値は false になっている
  • Android では画面外の UI コントロールのIDを指定して find_element() を呼び出すと見つからず、NoSuchElementError が発生する
    • 当たり前だが、displayed? の値は常に true になる

と、言うわけで、UIコントロールの属性をチェックする際に Android ではいちいち画面をスクロールさせなければなりません。

そんなわけで、UI コントロールを探す場合、Android を念頭に置き、スクロールしながら探すようなロジックを組み込む必要があります。

こんなコードで対応してみました

  def checkTextValuesWithScroll(id, value)
    startX = @driver.manage.window.size.width / 2
    startY = @driver.manage.window.size.height * 0.65
    offsetY = @driver.manage.window.size.height * 0.53

    retryCounter = 0
    begin
        element = find_element(:accessibility_id, id)
    rescue Selenium::WebDriver::Error::NoSuchElementError
        #画面をスクロールさせてもう一度取り直す
        puts "finding #{textInfo['id']}...retry:#{retryCounter}"
        Appium::TouchAction.new.swipe(start_x: startX, start_y: startY, offset_x: 0, offset_y: - offsetY, end_x: startX, end_y: startY - offsetY, duration: 1000).perform
        sleep 1
        retryCounter += 1
        # 10回までリトライする
        retry if retryCounter < 11
    end

    expect(element.text).to eq value
  end

この例では指定した id のUI要素が value のテキストを持っているかチェックしています。 id の値で find_element() を呼び出して、要素が見つからない場合は画面をスクロールさせて再度リトライするようにしています。 画面をスクロールさせるためのパラメータは画面全体の座標から計算していますが、スクロールさせる View の大きさと位置から計算する必要があるかもしれません。 10回までリトライするようにしていますが、この回数はアプリの縦の画面サイズに応じて適宜変える必要があります。

このサンプルでは Android ではスクロールしながら UI コントロールを探しますし、iOS ではスクロールせずに UI の文字列をチェックします。