Maestro 入門

# Maestro とは？

公式ドキュメントの内容

https://docs.maestro.dev/

# Maestro の導入

## 前提

- OS: macOS
- Xcode14 以上をインストールしておく

## maestro をインストール

前提として`homebrew`をインストールした上で、以下コマンドを実行して`maestro`をインストールします。(homebrewのインストールは[公式ドキュメント](https://brew.sh/ja/)を参照)
```console
brew tap mobile-dev-inc/tap
brew install maestro
```

以下公式ドキュメントを参考にしています。
https://docs.maestro.dev/getting-started/installing-maestro

# Maestro で行う UI テストを書く

## Maestro Studio による GUI ツールでのテスト作成

### Maestro Studio の起動

シミュレーターをあらかじめ起動しておく必要があります。
以下でシミュレーターを起動できます。(またはXcodeでアプリを実行して任意のシミュレーターを起動しましょう)
`open -a Simulator`

https://qiita.com/YMori0811/items/8ae9f5ab556ad468ec56

Maestro の UI テストを GUI で作成するツールが、Maestro 側で用意されています。
以下コマンドで、そのツールを立ち上げます。
`maestro studio`

実行時ログは以下になります。
```console
% maestro studio
Running on iPhone 16 - iOS 18.2 - 566836CA-C153-4F8B-AFEA-FCD9966B1C55
```

複数のシミュレーターが立ち上がっている状態で、`maestro studio`を実行すると、以下のようにどのシミュレータで実行するかを選択することもできます。
```console
% maestro studio
iOS

[1] iPhone 16 - iOS 18.1 - 63E10B92-8C1E-4CC3-97A1-561536C41F7A
[2] iPhone 16 Pro - iOS 18.2 - D2AE3E98-6626-45C2-BDF9-0D1E522385E7

Multiple running devices detected. Choose a device to run on.

Enter a number from the list above:
```

`maestro studio`が立ち上がると、ブラウザの GUI ツールが立ち上がります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3359094/626e8b1e-73af-46e0-86d5-79e3b86cd059.png)


### Maestro Studio で動作確認する

試しにあるボタンをタップしてみると、以下のようなポップアップが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3359094/83ad5597-025b-415c-88a1-9f38f44541f1.png)


再生ボタンを押下すると、アプリの画面がボタンを押下した時の挙動をとります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3359094/26d0da29-9151-48a1-8f5b-95ef69447b22.png)


今回のアプリではボタンを押すと、別画面へ遷移するのでブラウザ上のアプリの画面も画面遷移しています。
また、右画面には先ほど実行したコマンドが表示されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3359094/f9270482-3ad8-4b4c-bd98-cd9bf3bb9bd0.png)


このコマンドが、Maestroテストを構成するコマンドになります。

以下のように、直接自分でコマンドを入力してコマンドの動作確認をすることもできます。
ちなみに以下は"Diary List"という文言が画面に表示されているかを検証するコマンドです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3359094/bfdf3a96-baf5-456a-adae-213a924e0b97.png)


アプリを自動で動作させる様々なコマンドがあるので、一度公式のリファレンスを見ることをお勧めします。
https://docs.maestro.dev/api-reference/commands

### Maestro Studio でテストコードを出力する

いろいろいじってみたらコマンドが溜まってきたので、今まで触ってきた操作を自動化してもらえると嬉しいですよね。
Maestro Studioで使用したコマンドは、実際にテストを行うときに使用するymlファイルにエクスポートすることもできます。

例として、まず以下のようにMaestro Studioで画面を操作します。すると右側にコマンドが溜まっていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3359094/a475fc82-3b70-411b-aa5d-443c9586e8a3.png)

実際にテストで使用したいコマンドにチェックをつけます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3359094/ff2c69e6-fc66-455a-ac20-e4d3c0a22f83.png)

チェックをつけたら、"Export"ボタンを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3359094/2cec23b6-6f96-4f0a-ad9a-004c02c03c7e.png)

以下のような画面が表示されるので、"Save"ボタンを押下するとymlファイルがダウンロードされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3359094/811af351-ddd3-41d6-aaa8-deaa89a15174.png)

export すると以下のような yml ファイルが出力されます。

```yml
appId: null
---
- tapOn: "graph_circle"
- tapOn:
    point: "11%,9%"
- tapOn:
    point: "14%,14%"
- tapOn:
    point: "66%,25%"
```

## UIテストを実行する

作成したテストコード(yml ファイル)を使って、UI テストを実行するには、以下コマンドを実行すれば OKです。

```console
maestro test <ymlファイルのパス>
```

上記実行時にも、シミュレーターは立ち上げておく必要ありです。

今回は以下のような yml でテストを行ってみます。

```yml
appId: <アプリのバンドルID>
---
# 初期状態にする
- clearState
# アプリ起動
- launchApp

- tapOn: "graph_circle"
- tapOn:
    point: "11%,9%"
- tapOn:
    point: "14%,14%"
- tapOn:
    point: "66%,25%"
```

実行結果は以下のような感じ
失敗したステップがあればその場で中断し、緑のチェックではなく赤のマークがつきます。

```console
Running on iPhone 16 - iOS 18.2 - 566836CA-C153-4F8B-AFEA-FCD9966B1C55

 ║
 ║  > Flow
 ║
 ║    ✅  Clear state of morimori.Macho
 ║    ✅  Launch app "morimori.Macho"
 ║    ✅  Tap on "graph_circle"
 ║    ✅  Tap on point (11%,9%)
 ║    ✅  Tap on point (14%,14%)
 ║    ✅  Tap on point (66%,25%)
 ║

╭───────────────────────────────────────────────────────────────────────────────╮
│                                                                               │
│   A new version of the Maestro CLI is available (1.39.13). Upgrade command:   │
│   curl -Ls "https://get.maestro.mobile.dev" | bash                            │
│                                                                               │
╰───────────────────────────────────────────────────────────────────────────────╯
```

# 検証方法

UI テストなので、何かしら検証する必要があります。
検証の主な手法としては、特定の要素が画面に表示されているかを確認することだと思います。
Maestro では、[`assertVisible`](https://docs.maestro.dev/api-reference/commands/assertvisible)というコマンドが提供されています。

例えば、アプリを起動して以下のような画面が表示されるとします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3359094/4f9c61a5-b8a1-490c-a64a-bd724290820e.png)


画面に"表示する日記がありません"という文言が表示されることを検証したい場合は、以下のような yml を書きます。

```yaml: main.yml
appId: <アプリのバンドルID>
---
# 初期状態にする
- clearState
# アプリ起動
- launchApp
# 検証
- assertVisible: "表示する日記がありません"
```

これで`main.yml`があるディレクトで`maestro test main.yml`を実行すると、以下のようにテストが実行され成功します。

```console
Running on iPhone 16 - iOS 18.2 - 566836CA-C153-4F8B-AFEA-FCD9966B1C55

 ║
 ║  > Flow
 ║
 ║    ✅  Clear state of morimori.Macho
 ║    ✅  Launch app "morimori.Macho"
 ║    ✅  Assert that "表示する日記がありません" is visible
 ║
```

以下のように、期待する文言を変えるとテストは失敗します。

```diff_yaml
 appId: <アプリのバンドルID>
 ---
 # 初期状態にする
 - clearState
 # アプリ起動
 - launchApp
 # 検証
- - assertVisible: "表示する日記がありません"
+ - assertVisible: "表示する日記がありまてん"
```

```console
Running on iPhone 16 - iOS 18.2 - 566836CA-C153-4F8B-AFEA-FCD9966B1C55

 ║
 ║  > Flow
 ║
 ║    ✅  Clear state of morimori.Macho
 ║    ✅  Launch app "morimori.Macho"
 ║    ❌  Assert that "表示する日記がありまてん" is visible
 ║

Assertion is false: "表示する日記がありまてん" is visible
```

assertion 系のコマンドは他にもいくつかあるので、必要なコマンドがないか公式リファレンスで調べてみると良いです。

https://docs.maestro.dev/api-reference/commands/assertnotvisible
https://docs.maestro.dev/api-reference/commands/extendedwaituntil

# 要素の指定方法について

Maestro でタップ(`tapOn`)や指定の要素があるかどうかの検証(`assertVisible`)を行うときなど、要素を指定することはたくさんあります(どの要素をタップするのか、どの要素が存在するのを検証するのかなどの用途)
Maestro では要素を指定方法をセレクターと呼び、いくつかの方法を提供してくれています。

公式ドキュメントでは以下方法で指定することができることを提示してくれています。

```console
- tapOn: # or any other command that works with selectors
    text: 'Text'     # (optional) Finds element with text or accessibility text that matches the regular expression
    id: 'the_id'     # (optional) Finds element with accessibility identifier that matches the regular expression
    index: 0         # (optional) 0-based index of the view to select among those that match all other criteria
    point: 50%, 50%  # (optional) Relative position on screen. "50%, 50%" is the middle of screen
    point: 50, 75    # (optional) Exact coordinates on screen. x:50 y:50, in pixels
    width: 100       # (optional) Finds element of a given width
    height: 100      # (optional) Finds element of a given height
    tolerance: 10    # (optional) Tolerance to apply when comparing width and height
    enabled: true    # (optional) Searches for view with a given "enabled" state
    checked: true    # (optional) Searches for view with a given "checked" state
    focused: true    # (optional) Searches for view with a given "focused" state
    selected: true   # (optional) Searches for view with a given "selected" state
```

https://docs.maestro.dev/api-reference/selectors

自分としては`id`にて`accessibility identifier`を使った指定方法が良いと思っています。
理由としては、最もテストが壊れにくい指定方法と考えるからです。
座標指定(`point`)だとデバイスのサイズによっては意図した要素を指定できないこともあるので、デバイス依存のテストになってしまいます。
また、表示文言指定(`text`)だと、ローカライズしているなどの事情があれば、デバイスの言語設定に依存してしまいます。(ローカライズしていないアプリでも、OS 依存のダイアログなどはデフォルトでローカライズされているので、この問題の影響を受ける)
`accessibility identifier`であれば、上記懸念はないので最も壊れにくいテストになると思われます。

# yml ファイルを分割する

確認項目が大きくなってくると、yml ファイルが巨大化してくることが予想できます。
それを防ぐために、画面毎などある程度の粒度で yml ファイルを分けられると嬉しいですね。

maestro では yml ファイルから別の yml ファイルのテストを呼び出すことが可能です。
実装例としては以下のような感じ。

```yml: main.yml
appId: <アプリのバンドルID>
---
# 初期状態にする
- clearState
# アプリ起動
- launchApp
# 別のymlファイルを呼び出す
- runFlow:
    file: "check_graph.yml"
```

```yml: check_graph.yml
appId: <アプリのバンドルID>
---
- tapOn: "graph_circle"
- tapOn:
    point: "11%,9%"
- tapOn:
    point: "14%,14%"
- tapOn:
    point: "66%,25%"
```

https://docs.maestro.dev/api-reference/commands/runflow

# 触ってみての感想

- UI テストを書くコストはかなり低いように感じました。Maestro の GUI ツールで自分の期待する操作を行っていると、Export 機能によって自動で UI テストを生成できるので、非開発者でも UI テストを作れそうという気持ちです。

- クロスプラットフォームに対応したツールなので、iOS と Android で共通で使うことも可能です。
ただ、実際 iOS と Android で完全に同じ UI になることはあまりないなと思っている(OS のダイアログが違っていたり、微妙に画面のフローが違っているのはあるあるかと)ので、
iOS と Android で共通化できるという目的で利用できるのはレアケースでしょう。
もしやるなら、テキストもしくは AccessibilityId を iOS/Android で完全に揃えて、画面の遷移フローなどもすべて同じである必要があると思います。

- OS で表示される一部画面ではうまく要素を認識してくれない問題に当たったりということがあったので、一部テストが難しい画面もあります。
例えば、HealthKit の権限モーダルは maestro が要素の位置をうまく検出できなくて、テキストや ID での選択ができず座標指定しか使えなかったです、、

- テストレポートについては、フロー毎(yml ファイルの粒度)に成功したかどうか程度の情報しか出せないので、テストカバレッジなどの高度なレポートは期待できません

- 実機で Maestro を使用することができないので、シミュレーター上で行えるテストしかできません


入門してみての所感として、手軽な UI テストツールではありますが、一部かゆいところには手が届かず複雑なテストには向かないです。
手軽さ重視で自動化できないところは手動で行うことも辞さない場合は、非常に良いツールなのではと思いました。
逆に手動テストをほぼ全て自動化したい、カバレッジをとってどれぐらいケースを網羅できているのかを知りたいといった用途では Maestro は向かないように思いました。

# おわり
今回は入門レベルでどんなことができるのか調べてみましたが、他にもできることがあるので応用編の記事を出す頃にはMaestroの感想も変わるかもです。
まだベータ版でしたが、AIの技術を用いた検証のコマンドがあったり、AIにテスト品質を解析してもらうといった機能もあったので、そのあたりはかなり楽しみですね。

https://docs.maestro.dev/api-reference/commands/assertwithai
https://docs.maestro.dev/cli/test-suites-and-reports#analyze