DockerでAndroidエミュレータを起動してconnectedAndroidTestを自動化する


こんにちはsekitakaです。
UnitTestだけでなくContextやActivityを使用するconnectedAndroidTestも自動化したいですよね。
しかしconnectedAndroidTestは実機 or エミュレータが必要なので少し自動化しづらく感じると思います。

この記事ではDockerでAndroidエミュレータを起動し、デプロイコンテナからconnectedAndroidTestを実行する方法について紹介します。
使用した[サンプルプロジェクト][sample]も公開していますので自由に使用してください。

# 実施した環境
Dockerホスト: macOS Sierra

# Dockerの構成
今回はアプリのデプロイ用のdeployコンテナと、Androidエミュレータが実行されるemulatorコンテナの2つを連携して、connectedAndroidTestを実施します。
よってdocker-composeで2つのコンテナの連携を行います。

## dockerディレクトリ
docker関連のファイルをプロジェクト直下のdockerディレクトリにまとめています。

```
docker
├── deploy
│   ├── Dockerfile
│   └── share_by_container
└── docker-compose.yml
``` 

### deploy
`deploy`ディレクトリにはデプロイ用のコンテナのDockerfileとdeployコンテナで永続化されるディレクトリ(`share_by_container`)があります。

Dockerfileの内容は以下の通り。
JDK8とAndroidSDKをインストールしています。

```docker/deploy/Dockerfile
FROM ubuntu:16.04

# JDKインストール
RUN apt-get -y update
RUN apt-get -y install openjdk-8-jdk

# Android SDKインストール
RUN apt-get install -y wget unzip
RUN wget https://dl.google.com/android/repository/tools_r25.2.3-linux.zip
RUN mkdir -p /Android/sdk
RUN unzip tools_r25.2.3-linux.zip -d /Android/sdk

# 環境変数
ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64
ENV ANDROID_HOME /Android/sdk
ENV PATH $ANDROID_HOME/bin:$PATH
```

### emulator
エミュレータはDocker Hubで公開されているイメージをそのまま使用するので、専用のディレクトリは作成していません。

### docker-compose.yml
docker-compose.ymlは次のように作成しています。

```yaml:docker-compose.yml
docker-compose run deploy bash deploy.sh 
version: '2'
services:
  # アプリのテスト/ビルド用コンテナ
  deploy:
    build: deploy
    volumes:
      - ./deploy/share_by_container/android-sdk:/Android/sdk
      - ./deploy/share_by_container/dot_gradle:/root/.gradle
      - ../:/app
    working_dir: /app
    # tty: true # コンテナにログインして調査したい場合はコメントイン
    depends_on:
      - emulator
  # エミュレータ実行コンテナ
  emulator:
    image: mattsawyer/android-emulator
```

deployコンテナはemulatorに依存する設定にしています。
エミュレータのイメージは[mattsawyer/android-emulator](https://hub.docker.com/r/mattsawyer/android-emulator/)を使用しています。
このエミュレータのイメージは展開すると8GBくらいになるので、ディスクを節約したい場合Dockerfileを調整して、必要なエミュレータのみ含むようにするとよいでしょう。

# deploy.sh
deployコンテナで`deploy.sh`を実行することでconnectedAndroidTestの実施をします。

```deploy.sh
#!/bin/bash

# Android Studioが生成する不要なファイルを削除
rm -f local.properties

# ローカル環境で同意したライセンス情報を書き込み
mkdir -p $ANDROID_HOME/licenses
echo "8933bad161af4178b1185d1a37fbf41ea5269c55" > $ANDROID_HOME/licenses/android-sdk-license

# Android系のコマンドへアクセスできるようにパスを追加
export PATH=$PATH:/Android/sdk/platform-tools:/Android/sdk/tools/bin

# emulatorにadb接続し、deviceの準備が出来るまで待機
ADB_DEVICES=""
adb connect emulator:5555
until grep -q "emulator:5555 device"<<<$ADB_DEVICES;
do
  echo "waiting emulator"
  adb connect emulator:5555
  ADB_DEVICES=`adb devices`
  sleep 5
done
adb devices
sleep 60 # apkのインストール可能になるまで待機

# connectedAndroidTest実施
./gradlew clean && \
./gradlew connectedAndroidTest
```
emulatorが準備できるまで待機しなければならないので、このようになっています。
あくまでも検証した環境では`sleep 60`で十分でしたが、他の環境では調整が必要かもしれません。
またemulatorコンテナを再利用すれば、このように待つ必要はなさそうです。

# connectedAndroidTestの実施
いよいよ実施です。
以下のコマンドを実行するとemulatorコンテナとdeployコンテナが起動し、deployコンテナのdeploy.shが実行されます。deploy.shでconnectedAndroidTestを実行しているので、テストの実行ができます。

```bash
docker-compose run deploy bash deploy.sh || docker-compose kill
```
`kill`しているのはemulatorコンテナが起動したままになってしまうからです。

以下のようなテスト成功の出力が得られれば成功です。
今回はAndroid Studioで新規作成したばかりのプロジェクトだったので、1testsのみ実行されました。

```
Starting 1 tests on Android SDK built for x86 - 4.4.2
:app:connectedAndroidTest

BUILD SUCCESSFUL
```

# まとめ
いかがでしたでしょうか。 Docker上で動作するようにしておけば、他の環境でも簡単にCIできる環境ができあがるので、オススメの方法です。またjenkinsのホストも汚れなくて済みます。
少しデプロイに時間がかかるのがデメリットですが、是非CIに取り入れていきたいですね。

[サンプルプロジェクト][sample]も公開しているので自由に使用してください。


[sample]:https://github.com/sekitaka/DockerConnectedAndroidTest