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

ローソンデジタルイノベーション(LDI)の技術ブログです

Appium1.xからAppium 2.0に移行してみた(appium/java-client編)

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

前回「Appium1.xからAppium 2.0に移行してみた」の記事で、Appium2.0への移行が簡単だったよという話をしましたが、あれはAppium Serverの話でした。

当社ではテストコードはKotlin、クライアントソフトウェアは appium/java-client を使用しています。先日java-clientがメジャーバージョンアップしていたので、この度 7.6.0 から 8.0.0 へアップグレードしてみました。

github.com

APIの仕様が変更されており、マイグレーションが必要だったので、コード修正に少し手間がかかりました。本記事では修正内容を具体的に紹介したいと思います。

マイグレーションガイド

Appiumの公式にマイグレーションガイドが出ているので一読をお勧めします。

github.com

基本的にはこちらに書かれていることが変更点となりますが、あまり具体的な移行手順は書かれていません。

すでに作成済みの自動テストに新しいバージョンのappium/java-client を取り込んでビルドすると、山のようにビルドエラーが出ます。
最初に絶望があります。
次に、ガイド片手にエラー箇所を確認していけば修正方針が理解できます。

ちなみに新バージョンを使用するにはbuild.gradle.ktsの以下の箇所を修正します。

implementation("io.appium:java-client:8.0.0")

W3Cへ標準への準拠

8.0.0ではAPIに大きな変更が行われていますが、マイグレーションガイドに書かれているように、その理由はW3C標準への準拠にあります。

AppiumはプロトコルがJSON Wire Protocolを使用している点でSelenium互換だとされています。これらの製品が一定の成功を収めたことで、最近ではW3CでWebDriverの標準化が進んでおり、Selenium 4がこれに準拠し、この流れでappium/java-clientもバージョン8.0.0でW3Cの標準仕様に対応しました。

AppiumDriverの型変更, MobileElement の廃止

appium/java-client の場合WebDriverの仕様変更はAppiumDriverクラスの仕様変更を意味するようです。

AppiumDriver<MobileElement>

がエラーになっている箇所は、型引数の指定を削除して単に

AppiumDriver

にするとエラーが解消します。

従来はAppiumDriverで要素を取得するとMobileElementが返ってきましたが、MobileElementは廃止されてW3C準拠のWebElementに置き換わりました。AppiumDriverが扱う要素の型はWebElement以外のバリエーションは存在しなくなったため、型引数の指定が不要になったようです。

findElementBy系のショートカットメソッドの廃止

findElementByClassName とか findElementById とかのショートカットメソッドが廃止されたので、使用している箇所がビルドエラーになります。 findElement(By) または findElement(AppiumBy)を使用するように変更が必要です。ショートカットメソッドの使用を多用している場合は修正量が多くなりますが、難しい作業ではありません。書き換えで凡ミスした場合は、自動テスト実行時にテストが落ちるので、都度確認して修正すれば問題ないです。

resetApp/launchApp/closeApp の廃止

appium/java-client 8.0.0からこれらのメソッドが使えなくなりました。当方はこれらのメソッドを多く使用していたので、廃止されたことは少し驚きました。 マイグレーションガイドで下記のリンクを辿ると、廃止に至る経緯が記載されていました。

github.com

具体的に何が問題なのか細かいことは書かれていないですが、appium/java-clientのこれらのメソッドはもはやAppiumの標準的なセッション管理に対応しておらず危険なので削除するよ ってことのようです。

当方では、自動テスト実行時間の短縮のため、各テストメソッドの実行ごとに毎回テストセッションを張り直すのではなく、セッションは原則として張りっぱなしの状態にして、resetAppでテスト対象アプリを再起動してから次のテストメソッドを実行するということを行なっていたので、正直厳しい変更内容でした。

代替策としては

  1. 毎回AppiumDriverのインスタンスを生成してセッションを張り直すように書き換える

  2. terminateAppでアプリを終了してからアプリアイコンをタップしてアプリを起動するように書き換える

の2つが考えられましたが、1の方法だと毎回数秒〜十数秒程度のペナルティが発生してテスト実行時間が遅くなるのが目に見えていたので、少々面倒ですが2の方法で改修を行いました。結果、resetAppを使用するよりは遅いですが、1の方法よりは速いとう程度になりました。

"appium:"プレフィックス

W3C標準への準拠によって、W3C標準ではないCapabilityにはプレフィックスとして"appium:"を付けることが必要になりました。 appium/java-client 8.0.0を使用してAndroidDriverを初期化するKotlinのコード例を以下に示します。

        val caps = DesiredCapabilities()
        with(caps) {
            setCapability("appium:automationName", "UiAutomator2")
            setCapability("appium:appPackage", "com.android.settings")
            setCapability("appium:appActivity", "com.android.settings.Settings")
        }

        val androidDriver = AndroidDriver(URL("http://127.0.0.1:4723/"), caps)

        val e = androidDriver.findElement(AppiumBy.xpath("//*[@text='ネットワークとインターネット']"))
        println(e.text)

この変更については単純に書き換えるだけで良いので、特に難しくはありませんでした。

ちなみに、7.6.0のバージョンだと"platformName"と"platformVersion"が必須だったのですが、8.0.0だとこれらの指定を省略しても起動できるようになりました。

同様にIOSDriverを初期化するコードを以下に示します。

        val caps = DesiredCapabilities()
        with(caps) {
            setCapability("appium:automationName", "XCUITest")
            setCapability("deviceName", "iPhone 13")
            setCapability("appium:bundleId", "com.apple.Preferences")
        }

        val iosDriver = IOSDriver(URL("http://127.0.0.1:4723/"), caps)

        val e = iosDriver.findElement(AppiumBy.xpath("//*[@value='一般']"))
        println(e.text)

これらのコードを実行するにはAppium 2.0を使用する必要があります(Appium 1.xを使用するとエラーになります)。

こちらの記事の内容を先に実施しましょう。 Appium1.xからAppium 2.0に移行してみた - ローソンデジタルイノベーション テックブログ

まとめ

  • 当方の既存のテストコードは上記の作業を行うことでappium/java-client 8.0.0 に移行することができました。

  • appium/java-client 8.0.0では上記以外にも変更されている箇所がありますが、当方のテストコードではそれらを使用していないため影響はありませんでした。

  • 移行作業は基本的には単純作業が多いですが、resetApp/launchApp/closeAppの廃止 の影響を受ける場合は代替策1又は2を実施する必要があります。