App Crawlerによる自動UIテストにスクリプト制御を加える

 - そもそもApp Crawlerとはなんぞや？という方は[こちらの記事](https://qiita.com/kafumi/items/fff5324a4bb70b7b7986)がわかりやすいと思います。簡単に言うと、Androidアプリをテストコード無しに自動UIテストしてくれるもの。



App Crawlerに関して調べていて、[こちらのスライド](https://speakerdeck.com/tkmnzm/introduce-jetpack-app-crawler-tool?slide=7)の「スクリプトを用意することで制御をいれることも可能（ログインとか）」の一文を見つけ、App Crawlerによる自動UItestにスクリプト制御を入れてみることにした。<br>
が、App Crawler用の公式ドキュメントが用意されておらず、Firebase Test Lab のRoboテストのドキュメントやApp Crawlerのソースコードを見ながら苦労したので共有。

# 手順
1. Roboスクリプトを作ります。これはもともとFirebase Test LabのRoboテストにスクリプト制御を入れるためのスクリプトで、Android Studioで作れます。([作り方](https://firebase.google.com/docs/test-lab/android/robo-ux-test#scripting)の1~4までを行う。)
2. 1で作ったスクリプトをApp Crawler実行時にオプション--robo-script-fileを付けて渡してやる。<br>
例 : 

```bash
java -jar ./app-crawler/crawl_launcher.jar --android-sdk $ANDROID_HOME --app-package-name com.kkkkan.hogehoge --robo-script-file MainActivity_robo_script.json
```

以上。


# ハマったポイント
恐らくこれはRoboスクリプトのバグだと思うのだが、画面に表示されているViewに表示されている文字列が日本語だとRoboスクリプトのtextフィールドが文字化けする。(ボタンのandroid:textが「同意する」の時など。)
スクリプト制御でタッチしてほしいViewのtextフィールドが文字化けしているとうまく動かない。(ほかの部分は化けてても大丈夫っぽい。)

- この場合、textフィールドを空文字列にするとうまく動く。(ただし、タッチしてほしいViewにアプリのソースのlayoutファイルの方でちゃんとresource idを設定しておかなきゃダメ。)
- Roboスクリプトの構成としては、elementDescriptorsフィールドの配列の最初の要素がタッチされるViewになっているみたいだ。


例 : 
(Before)

```json
[
  {
    "eventType": "VIEW_CLICKED",
    "timestamp": 1569478244303,
    "replacementText": "���߂",
    "actionCode": -1,
    "delayTime": 0,
    "canScrollTo": false,
    "elementDescriptors": [
      {
        "className": "androidx.appcompat.widget.AppCompatButton",
        "recyclerViewChildPosition": -1,
        "adapterViewChildPosition": -1,
        "groupViewChildPosition": 0,
        "resourceId": "com.kkkkan.hogehoge:id/button",
        "contentDescription": "",
        "text": "��"
      },
      (後略)
```

(After)

```json
[
  {
    "eventType": "VIEW_CLICKED",
    "timestamp": 1569478244303,
    "replacementText": "���߂",
    "actionCode": -1,
    "delayTime": 0,
    "canScrollTo": false,
    "elementDescriptors": [
      {
        "className": "androidx.appcompat.widget.AppCompatButton",
        "recyclerViewChildPosition": -1,
        "adapterViewChildPosition": -1,
        "groupViewChildPosition": 0,
        "resourceId": "com.kkkkan.hogehoge:id/button",
        "contentDescription": "",
        "text": ""
      },
      (後略)
```


# 今回知った事
現在、[App Crawlerのオプションについて公式ドキュメントにも記述があるが](https://developer.android.com/training/testing/crawler#crawler-options)、実はここで紹介されているよりも沢山のオプションがあるらしい。<br>
https://dl.google.com/appcrawler/beta1/app-crawle.zip でapp-crawler.zipをダウンロードし解凍 →　更にその中のcrawl_launcher.jarをzipファイルとして解凍してみた。すると、
app-crawler\crawl_launcher\androidx\test\tools\crawler\launcher\setup\CrawlSetup.javaにApp Crawlerのオプションと思われるものがたくさん定義されていた。
以下、そのコピー

```java
public abstract class CrawlSetup {
    public static final String DEVICE_OUTPUT = "/sdcard/app_firebase_test_lab";
    public static final String ROBO_TEMP_FILES = "/sdcard/robo_tmp_files";
    private static final String APP_PACKAGE_NAME_FLAG = "--app-package-name";
    private static final String APK_FILE_FLAG = "--apk-file";
    private static final String GUIDE_FILE_FLAG = "--guide-file";
    private static final String TEXT_GUIDE_FILE_FLAG = "--text-guide-file";
    private static final String ROBO_SCRIPT_FILE_FLAG = "--robo-script-file";
    private static final String EXPERIMENTS_FILE_FLAG = "--experiments-file";
    private static final String CRAWL_TIMEOUT_FLAG = "--timeout-sec";
    private static final String OUTPUT_DIR_FLAG = "--output-dir";
    private static final String UI_AUTOMATOR_MODE_FLAG = "--ui-automator-mode";
    private static final String INSTANT_APPS_MODE_FLAG = "--instant-apps-mode";
    private static final String TEST_ACCESSIBILITY_FLAG = "--test-accessibility";
    private static final String WAKE_DEVICE_FLAG = "--wake-device";
    private static final String PAUSE_FLAG = "--pause";
    private static final String DEVICE_NAME_REGEX_FLAG = "--device-name";
    private static final String DEVICE_SERIAL_CODE_FLAG = "--device-serial-code";
    private static final String PRINT_NON_SDK_USAGE = "--print-non-sdk-usage";

　(後略)
```


いつか時間があったら、他のオプションについてもどんなものなのか調べてみたい。


# 感想
- 初回起動時には、自動的に立ち上がる利用規約画面で同意ボタンを押さないと次に進めないアプリをApp Crawlerでテストしようとしていて、今まで制御を入れないとどうしても同意しないボタンを押されてしまい初回起動時に関して自動テストできなくて困っていたが、これでできるようになってうれしい。
- Roboスクリプトをオプションで渡せばいいのであろうことは雰囲気ですぐわかったが、肝心のオプション名が「恐らく存在しているであろうオプション名がまとまって定義されているクラスをcrawl_launcher.jarを解凍して探せばいいのでは…？」と気づくまで、ググってみたりあてずっぽうでそれっぽいの入れてみたりしてもうまく探し当てられなくて大変だった。<br>--helpってしてcrawl_launcher.jarを実行してみても無反応だったし。
- そもそも、公式ドキュメントにちゃんと全てのオプションの記述を入れるか、crawl_launcher.jarに--helpオプションを作ってほしい。


# 参考にしたもの
https://speakerdeck.com/tkmnzm/introduce-jetpack-app-crawler-tool
https://qiita.com/kafumi/items/fff5324a4bb70b7b7986