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


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

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

実施した環境

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をインストールしています。

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は次のように作成しています。

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を使用しています。
このエミュレータのイメージは展開すると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を実行しているので、テストの実行ができます。

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に取り入れていきたいですね。

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