🏖️

Maestroで快適なiOS/AndroidのUIテストを!

2022/12/28に公開

こんにちは!テラーノベルでiOS/Android/Webとフロントエンド周りを担当している @kazutoyoです!

iOS/Androidといったモバイル向けのUIテストって作るのも大変だし、メンテナンスも大変ですよね🤯
それらをいい感じにメンテナンスしてくれるサービスもあり、テラーノベルでは導入を検討をしていました。

そんな中、今回紹介するMaestroを見つけ、これは…!となったので今回はそちらのお話をします。

🔎 Maestroとは

MaestroはiOS/Android用のUIテストフレームワークです。
iOS/Androidのネイティブアプリケーションだけではなく、FlutterやReact Nativeなどのクロスプラットフォームで動作するアプリケーションにも対応しています。

こちらが実際にテストを記述して実行した様子。かんたん!

🎖️ Maestroの特に優れている点

以下の点が他のUIテストフレームワークと比較して優れていると感じました。

  • 🤖 ネットワークの遅延や表示の遅れなどで自動的に待機し、実際に表示されてから実行するといった機能がある
    • sleep()特定の要素が表示されるまで待つ といった処理を毎回書かなくても良い!
  • ⚡ YAMLでテストファイルを構築できる
    • インタプリタ方式であり、毎回ビルドを走らせることもなく、反復的にテストを書くことができる
    • YAMLでの定義はFlowと呼ばれており、YAMLを分割してネストされたFlowもつくることができる
    • YAMLでの定義なので、iOSとAndroidで同じような画面構成のときに、YAMLファイルを複製して少し変更をするだけ(場合によってはそのまま)でiOS/Androidの両方に対応ができる!
  • 🔥 Maestro StudioでかんたんにUIテストをつくることができる
    • 現在のMaestro Studio(1.8.1)ではFlowを書くための補助的なStudio機能があります。
    • 次期バージョンでは画面上でポチポチするだけでFlowを作成することが出来るようです

🚶 Maestroを試してみる

こちらのインストール方法を参考に、Maestroを自分の環境にインストールします。

インストール後、 maestro コマンドでMaestro CLIが使えるようになります。

まずはサンプルアプリケーションを試してみましょう!
こちらを参考に、サンプルアプリケーション(Wikipedia)をもとにUIテストを作ってみます。

以下の maestro download-samples でサンプルファイルをダウンロードし、WikipediaをiOSシミュレータ/Androidエミュレータにインストールして実行します。

maestro download-samples

cd ./samples

## iOSのとき
unzip sample.zip
xcrun simctl install Booted Wikipedia.app

## Androidのとき
adb install sample.apk

そして、以下のFlowファイルを実行します。

## iOSのとき
maestro test ios-flow.yaml

## Androidのとき
maestro test android-flow.yaml

次のようにアプリが起動しました! 🎉

次はFlowを編集して、オンボーディング画面を進めてみましょう。
ここでテスト実行時に -c オプションをつけてContinuous Modeで起動することで、反復的に実行を繰り返すことが出来ます。

appId: org.wikipedia
---
- launchApp:
    # アプリの状態をクリアする
    clearState: true
- tapOn: "続ける"
- tapOn: "続ける"
- tapOn: "続ける"
- tapOn: "始めよう"

オンボーディングを進めることができましたね! 🎉

🖥️ Maestro Studioを試してみる

v1.17.4でのMaestro Studioでは、画面上の要素を取得し、そちらをFlow上でどのように書くかのサジェストを行うようなものになっています。

maestro studio コマンドを実行すると、ブラウザでMaestro Studioが表示されます。

次のようにブラウザ上に画面の表示がされ、要素をクリックするとFlowの定義例が表示されます。
これを使えばFlowの組み立てもかんたんですね!

また冒頭で述べたように、時期のMaestroではv1.21.0以降でInteractable Modeが追加されており、Maestro Studio上でインタラクティブにFlowを作ることが出来ます。凄い!🔥

🚚 その他Maestroの機能

画面録画

Flowの実行を録画することができます。
機能の紹介だったり、チームメンバーにアプリの挙動を共有するときなどに便利そうですね👀

maestro record ios-flow.yaml

テストスイートとレポート

いくつかのFlowをまとめて実行するときは、 maestro test にディレクトリを指定します。
https://maestro.mobile.dev/cli/test-suites-and-reports#running-multiple-tests

maestro test myFolderWtihTests/

またテスト結果のレポートをJUnit形式に出力することも出来ます。
https://maestro.mobile.dev/cli/test-suites-and-reports#running-multiple-tests

Maestro Cloud

各種サービスと連携し、実行してくれるMaestro Cloudも用意されています。
手軽にMaestroを実行するにはおすすめのようです。

😱 iOS/Androidで日本語(unicode characters)が入力できない問題

iOSで日本語が入力できない問題のWorkaround

Maestroは凄い良さそう!ってここまで話していたのですが、1点問題があります。
テキストの入力を行う inputText で日本語を扱うことが出来ません😢
こちらはMaestroが内部的にidb [1]を使っており、  inputText ではidbでキーコードを送る[2]ため、ASCII文字しか送ることが出来ません。

WorkaroundとしてCustom URL Schemeで入力する文字を渡し、それをフォーカスされているViewにセットする方を考え、試してみたところうまく動いたので、現在のバージョンでは次のように回避しています。

まず以下のようなFirstResponderを取得するExtensionをつくり、
UIView+FirstResponder.swift

import UIKit

extension UIView {
    func currentFirstResponder() -> UIResponder? {
        if self.isFirstResponder {
            return self
        }

        for view in self.subviews {
            if let responder = view.currentFirstResponder() {
                return responder
            }
        }

        return nil
    }
}

AppDelegateでURLスキームをハンドリングし、firstResponderとなっている要素にテキストをセットします。
SceneDelegate.swift

extension SceneDelegate {
    func handleCustomScheme(_ url: URL) {
        let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
        if url.scheme == "maestro-sample-app",
           url.host == "input-text",
           let inputText = components?.queryItems?.first(where: { $0.name == "text" })?.value,
           let window = UIApplication.shared.windows.first(where: { $0.isKeyWindow }),
           let firstResponder = window.currentFirstResponder() {
            if let textField = firstResponder as? UITextField {
                textField.text = inputText
            } else if let textView = firstResponder as? UITextView {
                textView.text = inputText
            }
        }
    }
}

MaestroのFlowとしては、 openLink でURLスキームを実行します(テキストもURLエンコードをする必要があります)

appId: jp.kazutoyo.sample.MaestroSample
---
- launchApp
- tapOn: "テキストを入力"
- openLink: "maestro-sample-app://input-text?text=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF%EF%BC%81"

こちらで日本語入力ができました! 🎉

☺️ 感想

Maestroは2022年9月に一般公開されたばかりでまだ安定しているとは言えませんが、公式のSlackチャンネルなどでも日々活発にディスカッションや質問などがされており、今後さらに発展・安定化されていきそうです。

Maestroはメンテナンスのコストなども既存のUIテストフレームワークに比べて圧倒的に良いと感じるので、将来的に非常に良い選択肢になるのかなと思います!

脚注
  1. Maestro v1.8.0でiOSのDriverが一部書き換えられ、一部XCUITestが使われるようになっています。
    https://blog.mobile.dev/maestro-re-building-the-ios-driver-603998b012e8 ↩︎

  2. v1.8.1時点ではこのようにinputTextの文字列をキーコードをidb経由で送るようになっています ↩︎

テラーノベル テックブログ

Discussion