BitriseでReactNativeのEspressoUITestを走らせてみた話（Android編

# BitriseでReactNativeのUITestを走らせてみた話

[AutoScale,Inc.](https://autoscale.jp/)でアプリの運用から開発をやっている[@t0m0120](https://twitter.com/t0m0120)です！

前回のレビュー施策に引き続き今回は[ReactNativeTokyo#6](https://r-n.connpass.com/event/141558/)で登壇させていただいた際のスライド[ReactNativeアプリをBitrise上でUITestしてみた話](https://speakerdeck.com/t0m0120/reactnativeapuriwobitriseshang-deuitestsitemitahua)の詳細記事という感じです。

**長すぎたので一旦Androdのみにしました**


WF-1000XM3ノイキャンやばすぎて集中力30%ぐらいバフされてる気がします。


# そもそもなぜUITest走らせたくなったのか

1. iPhone/Android端末多すぎ問題  
デザインの修正などいちいち確認するのが面倒だけど崩れとか最低限よく使われる端末は安定させたい。

2. NativeBaseのButtonにtransparent使っていると32bit(?)で落ちる問題  
多分32bit？のAndroid端末でクラッシュしていたみたいで気をつけていなかったのもあるがリリースしてしまった。32bit端末のシェアも少ないがレビューの☆１で気づき数時間で修正pushできたから良かったが事前に防ぎたい。今後はAndroidアプリも審査3日間かかるなど危険そう。
[Button.js theme backgroundColor: null crashing on RN 0.59.1 Android NoSuchKey Exception: backgroundColor #2673](https://github.com/GeekyAnts/NativeBase/issues/2673)

という2点の理由から事前に気づける環境がない事が悪いと思い
UITest走らせたいなー~~ReactNativeTokyoのLTネタになるしな~~と思い1週間ぐらいかけてUITestをBitrise上で走らせてみた。


# まず技術採用

## テスト走らせる場所として

[AWS Device Farm](https://aws.amazon.com/jp/device-farm/)が会社でポイントがあり使ってみたが実際にapkをアップロードしてブラウザ上で実機を動かせたりして便利だったがポイントがあるとはいえ高いというのとremote accessに３２bit端末が無くprivate_deviceを使うにはお問い合わせが必要だったので保留
（今後特定の端末でのバグを実機でちょっと触りたいなという場面が来たらremote_accessで使いそう

次に[Firebase TestLab](https://firebase.google.com/docs/test-lab/?hl=ja)を候補にあげた。が

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226100/7de9fe41-83be-769e-50d8-890f7bc0d151.png)

無料枠だと1日5件か10件
デバイスだと1時間1~5$と毎日走らせたりビルドのたびに走らせてチェックするにはちょっとお金かかりそうかなーと思っていたところに


普段使っている[Bitrise](https://www.bitrise.io/)上に
[DeviceTestingAndroid](https://devcenter.bitrise.io/jp/testing/device-testing-for-android/)
と
[DeviceTestingIOS](https://devcenter.bitrise.io/testing/device-testing-for-ios/)
を発見

[Bitrise](https://www.bitrise.io/)を使っていれば特に金額もかからずBitrise経由でTestlab叩いているっぽい。
対応端末も

### Android
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226100/786dc09d-313a-5f41-c10c-82169f2ce686.png)

### iOS
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226100/fbb8555d-5100-4697-9f21-49b9f2f36e45.png)


と見た感じ一番小さいiPhoneSE ノッチありiPhoneX
Android３２bitのNexsus4と割と新しめにPixel2
があり最低限は確保されている感じがしたのでこちらを使ってみることにした。

最終的には普段はBitriseでUITestを常に走らせておいて
リリース前にBitrise build -> apkをTestLabにscriptでUPload
物理デバイス５台をテストしておくみたいな体制になりそう。

ちなみにSocialDogではBitriseはOrgStandard（約1万円）を使っています。
基本的にDeveloperでも大丈夫な気がしますがインターン二人と僕がデザイン系のタスクすると大渋滞起こしたり時々45分の制限を超えたり2個並列してビルドすると待ちあんまりないなーとOrgにしています。
（1マシン45分→2マシン90分）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226100/d03f9ddb-cad3-48f2-dd04-17ede2359974.png)


## テストコードを書く

### ReactNative

```jsx
<LoginButton
  testID='LoginButton'
  accessible,
  accessibilityLabel='LoginButton'
>

// 実際は
<LoginButton
  {...set_id('LoginButton')}
>
// でReleaseBuildはnull開発系でiOSはtestID/Androidはaccessibilityみたいにしてる。
```

上のように遷移するボタンに一通り`testID`と`accessible, Label`を追加しておく。


### Android

一番メジャーそうかつTestLabで押されている[Espresso](https://developer.android.com/training/testing/espresso/index.html)を採用しました。

[Espresso setup instructions](https://developer.android.com/training/testing/espresso/setup?hl=ja)の通りにTest周りの依存関係を入れてからテストファイルは作成せずに

[EspressoTestRecorder](https://developer.android.com/studio/test/espresso-test-recorder?hl=ja)のとおりにAndroidStudioからアプリを立ち上げた状態でTestRecorderを使って入れていき作成ボタンを押すと自動的にファイルを作成されるのでそっちを使ったほうがimportとか自動で楽なはず。

アプリを起動したスマホ（実機のほうが軽いし楽かも）を用意しておき
AndroidStudioのRunからRecordEspressoTest
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226100/e6af5b9c-cf76-85a9-7cf1-3cc7c302bfe6.png)
↓
操作していく
一部操作取れないボタンなどがあるかもしれませんが
TextInput周りは確実に取れたのでTextInputなどとりあえず1つでも要素を追加して
下記の様にトレースさせる。
取れるなら全部取ってしまう。
↓
<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226100/714e8de0-e19c-ec66-0630-9e727af2da3a.png' width='30%'>
上記の様に取れたらTestを作成すると依存関係などすべて入ったテストファイルが生成される。

ので取れなかった要素を自力で入れる

今回では下記の様な関数を別途用意してみました。

```java
    public void clickElement(String name) {
        try {
            ViewInteraction targetElement = onView(allOf(withContentDescription(name), isDisplayed()));
            targetElement.perform((click()));
        } catch(NoMatchingViewException ignore) {
            Log.d("ClickElement", "Viewを見つけられませんでした。");
        }
    }
```

画面のロード待ちなど泥臭くsleep処理を入れて3秒待たせるなどにした。
本来はViewが表示されたらとかにしたほうが良いです。。。

```java
    public void waitSleep(long millis) {
        try{
            Thread.sleep(millis);
        } catch(InterruptedException e) {
            System.out.println("sleepに失敗しました");
        }
    }
```


スクリーンショットに関しては
[Firebase Test Lab インストゥルメンテーション テストのスクリーンショットを作成する](https://firebase.google.com/docs/test-lab/android/test-screenshots?hl=ja)の通りに依存関係を入れて

```java
import com.google.android.libraries.cloudtesting.screenshots.ScreenShotter;


clickElement('LoginButton');
waitSleep(3000);
ScreenShotter.takeScreenshot(title, mActivityTestRule.getActivity());
```

でScreenShotが撮影できます。

実際のコードとしてはこんな感じ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226100/729f8018-2d41-6487-7d01-000ab48acfcc.png)
sleepをクリックの前後に入れたりしているので
この程度ならjavaがわからない~~自分~~人にでも
testID系振ってclickElementで押してscreenshotで撮ってぐらいで
投げれるはず・・・？？？


ある程度テストを書いたら
作られたテストファイルを右クリックでRunしてローカルでテスト走らせたりしてみます。


## Bitrise

TestFileが完成したら今度はBitriseに投げます。

やる事としてはReactNativeのBuildをBitriseに投げてある事前提ですが
1. `AndroidBuild`の部分を`Android for Device Testing`に変える
2. **`android-test.apk`にsignする** ← これに気づかず中々通らなかった

[Androidのデバイステスト/Bitrise](https://devcenter.bitrise.io/jp/testing/device-testing-for-android/)に書かれたとおりにやるだけです。

詰まった所とREADMEと違うところだけ上げていきます。

### STEP1のメージのON/OFFスイッチはなくなっている？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226100/fb54df48-68d5-67f6-6158-fa5829786d29.png)

### Stepを増やす際の検索時に検索右のトグルをALLにしないとDeviceTesting系出ない
<img width="769" alt="スクリーンショット 2019-09-04 0.26.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226100/77719995-2f78-daf4-f4a9-c49d9f1ddd59.png">

### Debug以外でのVariantでAssembleXXXXXXAndroidTestしたい

[テストするビルドタイプの変更](https://developer.android.com/studio/test?hl=JA#%E3%83%86%E3%82%B9%E3%83%88%E3%81%99%E3%82%8B%E3%83%93%E3%83%AB%E3%83%89%E3%82%BF%E3%82%A4%E3%83%97%E3%81%AE%E5%A4%89%E6%9B%B4)にある通り

```java
android {
    ...
    testBuildType "staging"
}
```

で設定できる。

ReactNativeの場合DebugBuildだとBundleしないとローカルでの開発サーバーがないと単体で動かず、`app/build.gradle`の`project.ext.react`まわりのbundleInDebugをtrueなどに変えれば行けそうだったが
テストのdebugのときのみ変えるなど面倒だったのとほぼReleaseBuildのStagingBuildを用意していたのでStagingBuildでTestを走る様に上記を追加してBitriseのBuild for UITestingのVariantもStagingに変更する。

test.apkにもsignする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226100/81a3a2b4-615e-20fd-3186-3ce71ec621c2.png)


あとは
1. TestDeviceを決める `Nexus4,22,ja,portrait`とか
2. TestTypeを `instrumentation`へ
3. signしたtest.apkのpathを入れる

走らせれば動く（はず）です。
Buildページの上のTestReportsのViewから
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226100/21f7e9e7-9cc8-95be-399e-c41f09d564c5.png)
Performanceや
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226100/ce1783c8-5f49-27cd-8a9a-a1af5b1ec91f.png)
VideoやScrennshotoが見れます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/226100/21e29631-5ade-7bd4-d8cd-d5371e62e646.png)

ただScreenShotはなぜか表示されなくなったので別途TestArtifactsからDownloadしている・・・
調査中・・・コメント頂けると・・・

## 運用と言い訳

先週実装してみて
今週から深夜にTestBuildを走らせる

リリースもBitriseでAppStore/PlayStoreに投げるまでをBitriseで自動化していて
リリースビルドが叩かれたら終了後にテスト走らせるなど行っています。

これで最低限の画面の遷移と崩れとかには気づけるかなーという感じ
細かいロジックに関してはjestとかstorybookにまかせていって
NativeのUITestはあくまで全体のチェックに使う感じの運用になりそう。


ブランチプッシュしたらいつもStagingBuild走らせる様に普段はしてるのですが、
そこにまでテストを流すとインターン2人と自分が揃ったとき大渋滞しそうだなーっというのがあるのでデザイン系ブランチで気になったらBuildとかかなーとここは要調整。


またもしテストがだめになったとしてもtestIDの削除とjavaとobj-cのファイルの削除だけで住むので
万が一腐ったとしてもそこまでコード汚すことはないはず。


TestCodeに関しては数秒待つクリックの繰り返しでコード自体はClickとShotの繰り返しできれいだが秒で待たせたり雑な感じなのでちょいちょい改善していく予定・・・
~~JavaとJavaScript全然ちゃうやん・・・~~
きれいなJavaのテストコードの書き方とかあったらコメント下さい！お願いします！


## 最後に

AutoScaleではReactを使ったWeb/アプリの開発を行うエンジニアを募集しています！！
属人化せずに自動で動いて直せるし簡単に捨てられるみたいな状態を常に維持していきたいという心が強いので一緒に~~楽する方法を~~開発してみませんか！？

興味があればWantedlyからご応募やお話だけでもぜひ！！！
[自社サービスのアプリ/WebサービスをReactで開発するメンバー募集！！](https://www.wantedly.com/projects/342812)





気合が溜まったらiOS編も書きます！