Android JetpackのApp Crawlerを動かしてみる

[Shibuya.apk #36](https://shibuya-apk.connpass.com/event/139617/) の [ひっそりJetpackに追加されていたApp Crawler toolの紹介](https://speakerdeck.com/tkmnzm/introduce-jetpack-app-crawler-tool) で紹介されていた [App Crawler](https://developer.android.com/training/testing/crawler) を手元で動かしてみたので、実行方法や、はまったことなどをシェアします。

# App Crawlerとは

App Crawlerは、テストコードなしで、自動でUIテストを実行してくれるテストツールです。Firebase Test LabのRobo Testで使われているツールが、おそらくほぼそのまま提供されています。詳しくは、[ひっそりJetpackに追加されていたApp Crawler toolの紹介](https://speakerdeck.com/tkmnzm/introduce-jetpack-app-crawler-tool)によくまとまっていますので、そちらをご参照ください。

2019年8月8日現在、App Crawlerはまだ正式リリースではなく、Beta 1.0という扱いのようです。App Crawlerの配布ZIPに含まれているREADMEにバージョンとリリースノートが記述されています。

# 実行方法

App Crawlerは実行可能Jarとして提供されています。[公式ドキュメント](https://developer.android.com/training/testing/crawler)にある通り、配布されているZIPをダウンロードして、そこに含まれている `crawl_launcher.jar` を `java` コマンドで起動することで実行できます。

```sh
# ZIPファイルをダウンロードして展開
wget https://dl.google.com/appcrawler/beta1/app-crawler.zip
unzip app-crawler.zip

# java コマンドで実行
java -jar app-crawler/crawl_launcher.jar <他にもいくつかオプションが必要>
```

なお、App Crawlerを実行するときには、テストを実行するデバイス (エミュレーターでも可) がADBで接続している必要があります。

# 必須のコマンドラインオプション

公式ドキュメントの[Crawler options](https://developer.android.com/training/testing/crawler#crawler-options)セクションに書いてあるように、App Crawlerの実行時には「Android SDKのパス」と「テスト対象のアプリ」を最低限、コマンドラインオプションで指示する必要があります。

## Android SDKのパス

`--android-sdk` オプションで指定します。

```sh
# --android-sdk オプションの例 (Macの場合)
java -jar app-crawler/crawl_launcher.jar --android-sdk ~/Library/Android/sdk <他にもオプションが必要>
```

## テスト対象アプリ

以下のどちらかの方法で、テスト対象のアプリを指定します。

1. APKファイルが手元にあるアプリを、デバイスにインストールしてテストする<br/>
   → `--apk-file` オプションでAPKファイルのパスを指定する
2. デバイスにインストール済みのアプリをテストする (APKファイルは不要)<br/>
   → `--app-package-name` オプションでパッケージ名を指定する

以下は、筆者が [Android Architecture Blueprints v2](https://github.com/googlesamples/android-architecture) で試した場合のコマンド例です。

```sh
# APKファイルをインストールしてテストする場合の例
java -jar app-crawler/crawl_launcher.jar --android-sdk ~/Library/Android/sdk --apk-file ~/Work/android/android-architecture/app/build/outputs/apk/prod/debug/app-prod-debug.apk

# インストール済みのアプリをテストする場合の例
java -jar app-crawler/crawl_launcher.jar --android-sdk ~/Library/Android/sdk --app-package-name com.example.android.architecture.blueprints.master
```

# テスト結果の出力

テスト結果は、`crawl_output` というディレクトリの中にファイルで出力されます。`crawl_output` 内には、例えば、次のようなファイルが生成されます。

```
crawl_output/
├── com.example.android.architecture.blueprints.master-logcat.txt
└── app_firebase_test_lab/
    ├── 1.png
    ├── 2.png
    ├── 3.png
    ├── accessibility1.meta
    ├── accessibility2.meta
    ├── accessibility3.meta
    ├── crawl_outputs.proto
    ├── crawl_outputs.txt
    ├── robo_actions.dpb
    ├── robo_crawls.dpb
    ├── robo_screen_elements.dpb
    └── robo_screens.dpb
```

`.txt`, `.png` 以外の拡張子のファイルはバイナリファイルで、少なくとも筆者は、バイナリの内容はわかりませんでした。今のところ、テスト実行中のlogcat出力 (`*-logcat.txt`) と、アプリのスクリーンショット (`*.png`) が、利用可能なテスト出力と言えそうです。

また、テスト実行中にアプリがクラッシュした場合は、テストがそこで中断し、コンソール出力にスタックトレースが出力されます。

# 筆者がはまった点

App Crawlerの実行にあたって、筆者がはまったポイントを紹介しておきます。

## はまりポイント(1): 古いプラットフォームがSDKにインストールされていると起動に失敗する

Android SDKにAPIレベル9 (Android 2.3) 以下のプラットフォームがインストールされていると、App Crawlerの実行に失敗します。

もっと正確に言うと、Android SDKの `platforms` ディレクトリ内に、`android-7` や `android-8` や `android-9` といったディレクトリがあると、実行に失敗します。

### 実行に失敗する理由

App Crawlerは、テストの実行前に、App Crawlerの処理をデバイス上で担うAPKをビルドしてデバイスにインストールする、という処理を行なっており、APKのビルドに、Android SDKにインストールされている最新のプラットフォームの `android.jar` を利用しています。

App Crawlerは、`android.jar` のパスを得るために「Android SDKにインストールされている最新プラットフォーム」のディレクトリを探すのですが、その処理が単純で、

1. `platforms` ディレクトリ内に存在するディレクトリを名前の辞書順に並べて、
2. 最後になったものを最新のプラットフォームのディレクトリとしている

ようです。

本当なら `android-29` などがヒットして欲しいのですが、`android-7` や `android-8` などのディレクトリが存在していると、そちらがヒットしてしまい、その中の `android.jar` がApp Crawler APKのビルドに使われることになります。App CrawlerはminSdkVersion = 18 のアプリなので、APIレベル10未満の `android.jar` ではビルドができず、結果、テストの実行に失敗する、というわけです。

## はまりポイント(2): 最近のOSバージョン上でないと実行できない?

App Crawlerの実行APKは minSdkVersion = 18 なので、Android 4.3 (APIレベル18) 以上のデバイスであれば実行できそうなのですが、筆者が手元で確認した限りでは、Android 7.0 (APIレベル24) 以上のデバイスでないとApp Crawlerが期待通りに動作しませんでした。テスト対象のアプリは起動してApp Crawlerが動きかけるのですが、テストの実行が即座に終了してしまいました。

App Crawlerはエラー検出 & エラー出力の実装がまだ不十分なようで、上記のケースでも特にエラーが出力されず、正常に終了したように見えています。何が悪いのかよくわかっていません。