Travis CI + Codecov で Android テストを自動実行してカバレッジを取得する

# 背景・目的

テストカバレッジを自動取得する方法を調査していたところ、 [Codecov](https://codecov.io) というテストカバレッジ取得 CI サービスがあることを知りました。

早速、 Android テストのカバレッジを取得する [Codecov のサンプルコード](https://github.com/codecov/example-android/tree/181e2d0299b66c3f715d7400442aa5baebe9d962) を確認したところ、 [Travis CI](https://travis-ci.org) 上で Android エミュレータが起動できず [build error](https://travis-ci.org/codecov/example-android/builds/371467562) の状態でした。

```shell:Build_#60
The command "./gradlew build jacocoTestReport assembleAndroidTest" exited with 0.
$ echo no | android create avd --force -n test -t $ANDROID_TARGET --abi $ANDROID_ABI
Error: Target id is not valid. Use 'android list targets' to get the target ids.


The command "echo no | android create avd --force -n test -t $ANDROID_TARGET --abi $ANDROID_ABI" exited with 1.
```

Android エミュレータを起動しないと実行できないテストがあるため、 Android エミュレータが起動できるよう、サンプルコードを修正しました。


# Android エミュレータとインストルメント化されたテスト

Android には、次の 2 つのタイプのテストがあります。[^1]

[^1]: [アプリのテスト / Test your app](https://developer.android.com/studio/test/)

* ローカル ユニット テスト / Local unit tests
* インストルメント化されたテスト / Instrumented tests

うち、インストルメント化されたテストについては、それを実行するために Android エミュレータが必要です。
（ローカル環境では、ハードウェア端末を接続してテスト実行することも可能です。）

2 つのタイプのテストの違いについては割愛しますが、 Context を参照するテストではインストルメント化されたテストを実装する必要があります。


# サンプルでエミュレータが起動できない原因

前述のエラーログには `Target id is not valid.` とありますが、これはエミュレータと同じ API level の Android SDK Platform がインストールされていない場合に表示されるエラーログです。

[サンプル の .travis.yml](https://github.com/codecov/example-android/blob/181e2d0299b66c3f715d7400442aa5baebe9d962/.travis.yml)

```yaml:.travis.yml
language: android
jdk: oraclejdk8
env:
  global:
    - ANDROID_TARGET=android-15 # エミュレータは API level 15 を指定
    - ANDROID_ABI=armeabi-v7a
android:
  components:
  - tools
  - platform-tools
  - build-tools-23.0.3
  - android-23                  # しかし、インストールする Android SDK Platform は API level 23 のみ
  - extra-android-m2repository
  - sys-img-${ANDROID_ABI}-${ANDROID_TARGET}
script:
  - ./gradlew build jacocoTestReport assembleAndroidTest
  - echo no | android create avd --force -n test -t $ANDROID_TARGET --abi $ANDROID_ABI
  - emulator -avd test -no-skin -no-audio -no-window &
  - android-wait-for-emulator
  - adb shell setprop dalvik.vm.dexopt-flags v=n,o=v
  - ./gradlew connectedCheck
after_success:
  - bash <(curl -s https://codecov.io/bash)
```

以上から、 `.travis.yml` の `components` に `android-15` を追記すれば解決すると考えましたが、これだけではエミュレータを起動することはできませんでした。

エラーログは表示されませんが、エミュレータが起動できない状態が 10 分以上続きタイムアウトします。この現象については詳細不明です。


# Travis CI 上で起動できる Android エミュレータについて

[Travis CI の公式](https://docs.travis-ci.com/user/languages/android/#how-to-create-and-start-an-emulator)には、次のように記載されています。

> Warning: At the moment, these steps are not fully supported by Travis CI Android builder.
> (警告：現時点では、これらの手順はTravis CI Android Builderによって完全にはサポートされていません。)
> 
> If you feel adventurous, you may use the script `/usr/local/bin/android-wait-for-emulator` and adapt your `.travis.yml` to make this emulator available for your tests.
> (あなたが冒険的であると感じるならば、あなたはスクリプト `/usr/local/bin/android-wait-for-emulator` を使い、あなたのテストのためにこのエミュレータを利用できるようにあなたの `.travis.yml` を適応させることができます。)
> 
> For example:
> 
> ```yaml:.travis.yml
> # Emulator Management: Create, Start and Wait
> before_script:
>   - echo no | android create avd --force -n test -t android-22 --abi armeabi-v7a
>   - emulator -avd test -no-audio -no-window &
>   - android-wait-for-emulator
>   - adb shell input keyevent 82 &
> ```

example に従い、 [API level 22 の Android エミュレータを指定](https://github.com/codecov/example-android/pull/13/files)したところ、[エミュレータを無事起動](https://travis-ci.org/codecov/example-android/builds/472095105)することができました。 Travis CI 上での Android エミュレータの起動は完全にはサポートされないが、 API level 22 のエミュレータであれば比較的安定して起動できるということでしょうか？残念ながら、これ以上の公式ドキュメントは見つけられず、具体的にサポートされる内容は不明です。


# Codecov によるテストカバレッジ取得

ここまでの手順により、 Android エミュレータの起動に成功しました。これにより、サンプルの Android テストを Pass することができ、[サンプルコード修正のプルリクエストへ Codecov からカバレッジが通知](https://github.com/codecov/example-android/pull/13#issuecomment-449851020)されました。サンプルコードはシンプルなため、テストカバレッジは 100% です。


# 結論

```diff:Pull_request_#13
diff --git a/.travis.yml b/.travis.yml
index 9a8faee..e665b4a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,7 +2,7 @@ language: android
 jdk: oraclejdk8
 env:
   global:
-    - ANDROID_TARGET=android-15
+    - ANDROID_TARGET=android-22 # エミュレータは API level 22 を指定する
     - ANDROID_ABI=armeabi-v7a
 android:
   components:
@@ -11,6 +11,7 @@ android:
   - build-tools-23.0.3
   - android-23
   - extra-android-m2repository
+  - $ANDROID_TARGET             # エミュレータと同じ API level の Android SDK Platform をインストールする
   - sys-img-${ANDROID_ABI}-${ANDROID_TARGET}
 script:
   - ./gradlew build jacocoTestReport assembleAndroidTest
```

# 今後について

今回は Codecov のサンプルにならって Travis CI 上で Android エミュレータを起動、テスト実行、カバレッジ取得をしました。有名な CI サービスは Travis CI だけでなく [CircleCI](https://circleci.com) などがあります。利用できるエミュレータが API level 22 だけでは困ることが起こり得るので、他の CI サービスを利用することで解決を図りたいです。