waveさんの技術日誌

wave1008の日記の新館です。

Compose MultiplatformとAppiumの相性の悪さに絶望している

このチュートリアルを参考に Compose Multiplatformでアプリを作って Appiumのテストをやってみようと思ったんですよ。

www.jetbrains.com



Android, iOSそれぞれのアプリの起動ができて おおっと思いました。

で、Appium Inspectorで中身をのぞいてみたんですよ。 そうしたら、iOSアプリのほうですが、中身に意味のあるものが無いんです。

<?xml version="1.0" encoding="UTF-8"?>
<AppiumAUT>
  <XCUIElementTypeApplication type="XCUIElementTypeApplication" name="ComposeDemo" label="ComposeDemo" enabled="true" visible="true" accessible="false" x="0" y="0" width="393" height="852" index="0">
    <XCUIElementTypeWindow type="XCUIElementTypeWindow" enabled="true" visible="true" accessible="false" x="0" y="0" width="393" height="852" index="0">
      <XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" accessible="false" x="0" y="0" width="393" height="852" index="0">
        <XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" accessible="false" x="0" y="0" width="393" height="852" index="0">
          <XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" accessible="false" x="0" y="0" width="393" height="852" index="0">
            <XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" accessible="false" x="0" y="0" width="393" height="852" index="0">
              <XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" accessible="false" x="0" y="59" width="393" height="759" index="0">
                <XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" accessible="false" x="0" y="59" width="393" height="759" index="0">
                  <XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="false" accessible="false" x="0" y="59" width="393" height="759" index="0"/>
                  <XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" accessible="false" x="0" y="59" width="393" height="759" index="1">
                    <XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="true" accessible="false" x="0" y="59" width="393" height="759" index="0"/>
                  </XCUIElementTypeOther>
                </XCUIElementTypeOther>
              </XCUIElementTypeOther>
            </XCUIElementTypeOther>
          </XCUIElementTypeOther>
        </XCUIElementTypeOther>
      </XCUIElementTypeOther>
    </XCUIElementTypeWindow>
    <XCUIElementTypeWindow type="XCUIElementTypeWindow" enabled="true" visible="false" accessible="false" x="0" y="0" width="393" height="852" index="1">
      <XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="false" accessible="false" x="0" y="0" width="393" height="852" index="0">
        <XCUIElementTypeOther type="XCUIElementTypeOther" enabled="true" visible="false" accessible="false" x="0" y="0" width="393" height="852" index="0"/>
      </XCUIElementTypeOther>
    </XCUIElementTypeWindow>
  </XCUIElementTypeApplication>
</AppiumAUT>

XCodeで作成したiOSアプリなら [Clic me!]の部分が XCUIElementTypeButton に [Compose: Hello, iOS 17.2!] の部分が XCUIElementTypeStaticText になるはずなんです。 でも、それらが存在していない。

これの意味するところは、Appiumのロケーターで要素を指定することが不可能ってことです。 つまり、Compose Multiplatformで作成したiOSアプリは、Appiumで自動テストすることができない。

困りましたね。 どうしたらいいんでしょうか。

Shirates (shirates-core) 日本語ドキュメント

Shiratesは、モバイルアプリのテストコードを簡単かつ楽しく書くことができる統合テストフレームワークです。 shirates-coreはコアライブラリです。

リポジトリ

shirates-core

概要

チュートリアル(Basic)

サンプル

推奨設定

プロジェクトの作成

テストクラスの作成

マクロ

セレクターとニックネーム

パラメーター

関数/プロパティ

ビヘイビアヘルパー

レポート

バッチ処理を作成する

チュートリアル(In action)

チュートリアル(Advanced)

ツールの設定

仮想マシンの設定

パッケージのインストール/アップデート

トラブルシューティング

Appendix

AppiumでiOS Simulatorの起動を速くする

AppiumでiOS Simulatorを起動するとき、初回起動あるいは久しぶりに起動した場合だと、かなりの待ち時間が発生します。

xcuitest driverがXcodeを起動してWebDriverAgent(WDA)をビルドするからです。

WDAのビルドが発生する場合の画面の起動時間

以下の手順で計測してみます。


1. iOS Simulatorを起動します。WebDriverAgentアプリがインストールされている場合はアンインストールします。


2. ターミナルでappiumを実行します。

appium


3. Appium Inspectorでcapabilitiesを以下のように設定し、[Start Session]をクリックします。(ストップウォッチ(物理)で計測開始。)

{
  "appium:automationName": "XCUITest",
  "platformName": "iOS",
  "appium:platformVersion": "16.4",
  "appium:deviceName": "iPhone 14(iOS 16.4)",
  "appium:bundleId": "com.apple.Preferences"
}


4. 設定画面が表示されます。(計測終了)


計測結果は以下のようになりました。

  • 38秒
  • 34秒
  • 39秒

平均 37秒

40秒近く待たされるのは悩ましいですね。

なお、使用したマシンのスペックは以下です。

usePrebuiltWDAオプションを使用する

Simulatorを複数起動する場合、SimulatorにWDAがインストールされていなければ、都度ビルドが発生してしまいます。

usePrebuiltWDAオプションを使用すると、ビルド済みのWDAを再利用してくれます。

appium.github.io


ビルド済みのWDAは以下のパスにあります。 ~/Library/Developer/Xcode/DerivedData/

capabilitiesに

  • usePrebuiltWDA
  • derivedDataPath

を追加で指定します。

derivedDataPathにはWebDriverAgent-で開始するフォルダのパスをコピーして設定します。

{
  "appium:automationName": "XCUITest",
  "platformName": "iOS",
  "appium:platformVersion": "16.4",
  "appium:deviceName": "iPhone 14(iOS 16.4)",
  "appium:bundleId": "com.apple.Preferences",
  "appium:usePrebuiltWDA": true,
  "appium:derivedDataPath": "~/Library/Developer/Xcode/DerivedData/WebDriverAgent-gbiraffrdhzcprhckdgrvrydwlaf"
}

ビルド済みのWDAを再利用する場合の画面の起動時間

usePrebuiltWDAオプションを有効にした場合、計測結果は以下のようになりました。

  • 9秒
  • 8秒
  • 9秒

平均 8.6秒

10秒切りました。比較するとだいぶ速いですね。
AppiumでiOSのテストの初回起動が遅くて
もやっとしている場合はどうぞ。

なお、当記事ではAppium Inspectorを使用して起動しましたが、テストコードから起動した場合も同じ効果が得られます。

Shiratesのアーキテクチャ概要

テスト実行構成の概要

shirates-core は Appium をドライバーとして使用します。

  • IntelliJ IDEA/Gradle
    • テストを実行するプロセスを起動します。
  • Appium Server
    • クライアントからの要求を受けてスマホ端末を操作します。
  • Emulator/Real Device
    • Appium Serverからの要求を受けて画面を操作します。

shirates-coreの依存ライブラリ

shirates-core はAppium以外にもいくつかのOSSライブラリを使用します。

shirates-coreの論理コンポーネント

shirates-core は以下のような論理コンポーネントから構成されます。

要素の取得とキャッシュ

shirates-core は画面上のすべての要素を取得してキャッシュします。

select関数等の要素の検索を実行する場合はキャッシュ内で行います。AppiumのfindElement系の検索メソッドは原則として使用しません。

キャッシュ内の要素情報を活用するすることで、Appium単体では実現できない複雑な検索を高速に実行することができます。

Android版とiOS版のテストコードの共通化

shirates~core は appium/java-client のラッパーとして機能し、AndroidとiOSの差異を吸収します。

AndroidとiOSの画面情報の差異は画面情報ファイルで吸収することができます。

これらの仕組みにより、同一仕様のアプリのAndroid版とiOS版のテストコードを共通化することができます。

Index

自動テストツールShiratesに関するブログ(Index) - waveさんの技術日誌

自動テストツールShiratesに関する開発者ブログ(Index)

Shirates(シラテス)はオープンソースソフトウェアとして提供されているスマホアプリ用の自動テストツールです。

本連載記事では公式ドキュメントを補足し、Shiratesの理解が深まる情報をブログ記事として提供します。 また、はてなブログ以外にポストした記事へのリンクも記載します。 なお、これらの記事は筆者が所属する組織の公式資料ではありませんのでご了承ください。

公式ドキュメント(日本語)

Shirates (shirates-core) 日本語ドキュメント

Index

外部サイトの記事

Shiratesの特長(README日本語訳)

この記事はGitHubのREADME.mdの日本語訳です。

github.com

Shirates (shirates-core)

Shiratesはモバイルアプリのテストコードを簡単かつ楽しく書くことができる結合テストフレームワークです。 shirates-coreはコアライブラリです。

機能を3行で説明

  1. テストコードを書くためのほぼクロスプラットフォームの便利なAPI、現在AndroidとiOSプラットフォームをサポートします。
  2. 強力なロギングとレポート機能を提供します。
  3. テスト環境を設定するための柔軟な設定フレームワークを提供します。

さらに詳しく説明

  • 画面の要素を操作し、その状態を確認するための便利なAPIを提供します。

  • 内部でAppiumを使用し、AndroidとiOSアプリのテストをサポートします。画面要素のマッピングファイルを使用して、AndroidとiOSのテストコードを統合できます。

  • テストセッションが終了した際に自動的にログ、スクリーンショット、HTMLレポート、MS-Excelワークシートを出力します。

  • MS-Excelワークシートでテスト結果を確認し、手動で追加テストを実行し、結果をワークシートに書き込むことができます。

  • 実際のテストを実行することなく、MS-Excel ワークシートにテスト仕様レポート(Spec-Report)を生成します。Spec-Report は、手動テストやレビューに便利です。そのため、テスト対象アプリが開発中であっても、テストコードを記述してSpec-Reportを出力し、手動テストを行うことができます。

Shiratesという名前の由来

このフレームワークのオリジナルはSHIRATAMA Test Frameworkという名前でした。私たちはこのオープンソース版をShiratesと呼んでいます。 SHIRATAMAは和菓子の白玉団子からとったもので、OKの頭文字のOを白玉団子に見立てました。

テストコード開発環境

ニックネーム指向の自動テスト

ニックネームは、テストコードを読みやすく、理解しやすくする Shirates の重要なコンセプトの 1 つです。画面、要素、アプリ、テストデータアイテムに対してニックネームファイルでニックネームを定義し、テストコードで使用することができます。ニックネームを使ったメッセージは、自然言語として読めるほどユーザーフレンドリーです。特に画面要素については、ニックネームを使うことで要素取得の実装の複雑さを隠すことができ、Android プラットフォームと iOS プラットフォームの違いを吸収することができます。その結果、あるプラットフォーム用のテストコードを書いた後に、他のプラットフォームへのギャップを埋めるための追加や修正をわずかな労力で行うことができます。

手動テストと自動テストの統合

テストエンジニアにとって、テストコードをコンパクトにまとめることは重要です。なぜなら、手動テスト用のドキュメントと自動テスト用のテストコードの両方を管理しなければならないからです。さらに言えば、ドキュメントとコードが 1 つの成果物に統合されているか、もう一方の成果物に変換できると理想的です。

Shiratesでは、テスト対象機能の実装完了に先立ってテストコードを記述し、NLR(No-Load-Runモード)でテストを実行し、ログをSpec-Report(MS-Excelワークシート)に変換し、自然言語でレビューすることができます。機能が実装されたら、まずはSpec-Report を使用して手動でモジュールをテストし、バグを報告して再テストします。並行して、実際に動作するテストコードを実装し、テストコードを実行し、テストコード自体をデバッグすることができます。製品モジュールとテストコードが修正されると、自動化されたリグレッションテスト環境が得られます。実行ログはSpec-Report形式に変換できます。主な結果は、OK、NG、MANUAL、SKIP、EXCLUDEDです。MANUAL は、手動でテストする必要があることを表します。SKIP は、テスト項目が何らかの理由でスキップされたことを表します。EXCLUDED は、そのテスト項目がセッションの範囲外であることを表します。このように、自動テストと手動テストの両方のテスト仕様とテスト結果が 1 つの MS-Excel ワークシートに統合されているため、テスト結果を簡単に集計することができます。

テンプレートコード生成(移行用)

既存の手動テストの仕様を Shirates のテストコードに移行する場合、ゼロから記述することもできますが、テンプレートコード ジェネレータを使用することもできます。テンプレートコードジェネレータは、Spec-Report 形式で記述された MS-Excel ワークシートを、シナリオ、ケース、条件、アクション、ターゲット、期待値のような構造的な関数呼び出しに変換し、Kotlin で JUnit 5 テストコードを出力します。それ以外の手順や記述はmanual関数として出力されます。manual関数を実際に動作する関数に置き換えることで、テストコードを動作させることができます。

Index

自動テストツールShiratesに関するブログ(Index) - waveさんの技術日誌

shirates-core日本語ドキュメント

shirates-coreの日本語ドキュメントが公開されました。

Shirates (shirates-core) 日本語ドキュメント

ドキュメントが英語なのがネックだった場合は 取り組みやすくなりました。