Espresso 2.0とRobolectricを併用したテスト環境の作り方

**2015.6.9追記：Android Studio 1.2 + Robolectric 3の組み合わせが良さそうなので、この記事をあまり鵜呑みにしないでください（[参考](https://github.com/robolectric/robolectric-samples/)）**


Androiderの皆さん、テストしてますか？

今回は、UIテストが幸せになるEspresso 2.0と、ユニットテストが幸せになるRobolectricをAndroid Studio的に自然な形で併存させるための設定をご紹介します。

ゴールとしては、下記のような表示になります。

![スクリーンショット 2015-02-03 17.27.05.png](https://qiita-image-store.s3.amazonaws.com/0/2276/1149d5d6-fbe9-5f96-3138-0487db45f653.png "スクリーンショット 2015-02-03 17.27.05.png")

Android Studioではあまり見かけないテストフォルダの配置に「おっ」と思った方は、次項を読み飛ばしてその先へどうぞ。

前提知識：これまでのテスト
----------------------

### Espresso

UIテストをモリモリ書いてきた諸兄は、Espressoと仲良くしていらっしゃることと思います。Support Libraryの一部としてEspresso 2.0が配布されるようになり、JUnit4が使えるようになったことを喜んだのは、記憶に新しいところです。

* [Espresso - android-test-kit - a fun little Android UI test API - Google's Testing Tools For Android](https://code.google.com/p/android-test-kit/wiki/Espresso)
* [続・AndroidでJUnit4を使う方法（Android SDKで正式サポートされました！）](http://qiita.com/AbeHaruhiko/items/91aa8fe0f2299969d0e7)

### Robolectric

また、ユニットテストをモリモリ書いてきた諸兄は、Robolectricと仲良くしていらっしゃることと思います。JVM上で高速にテストを回すことができるRobolectricは、手元でのTDDにおいても、サーバー上のCIにおいても、欠かすことのできない存在になってきていますね。

* [Robolectric](http://robolectric.org/)
* [Android - Robolectric に対応したプロジェクトを作る](http://qiita.com/KeithYokoma/items/79ab2d1d37e70f149fcb)

### 問題点と解決策（旧）

どちらもDB層からUI層まで幅広い領域のテストに対応していますが、動作環境や実行速度の一長一短があるため、上手く使い分けができると幸せを享受しやすそうです。

しかし、両方を扱うには、少し問題がありました。Android Plugin for GradleやAndroid Studioは、テスト用ソースディレクトリを1つしか認識することができなかったのです。JUnit3しか書けなかった昔のandroidTestと、JUnit4でテストを書くRobolectricをプロジェクト内に併存させるのは、難しいものに思われました。

この問題への一案を提示したのが、[robolectric/deckard-gradle](https://github.com/robolectric/deckard-gradle)というサンプルプロジェクトでした。

* src/test/
    * com.example/
        * espresso/
            * FirstUITest
            * SecondUITest
        * FirstRobolectricTest
        * SecondRobolectricTest

deckard-gradleでは、上記のようなパッケージ構造にした後、次のような設定を記述することで、実行されるテストの振り分けを行っていました。

```gradle:build.gradle
robolectric {
    include '**/*Test.class'
    exclude '**/espresso/**/*.class'
}
```

これらの設定により、下記のテストが可能になっていました。

* `./gradlew connectedAndroidTest`
* `./gradlew test`
* Android Studio上でのandroidTestの実行

これはこれで良いものでしたが、少し不自然な対応だったために、1つの問題が残りました。

Espresso用のパッケージを別に用意したために、パッケージプライベートなフィールドに直接アクセスすることによる、テスト上の利便性を享受できなくなってしまったのです。

```java:パッケージプライベートをテストで利用している例
@Test
public void 各Viewがインジェクトされている() {
    assertThat(activity.toolbar, is(not(nullValue())));
    assertThat(activity.tvDisplay, is(not(nullValue())));
    assertThat(activity.ivPicasso, is(not(nullValue())));
}
```

### 余談：その他の問題

他にも、旧来のdeckard-gradle方式による環境構築には、いくつかの問題がありました。

* RobolectricテストをAndroid Studio上から実行することができない（通常のRobolectricの問題）
* Espresso 2.0とRobolectricが混在した環境でAndroidJUnitRunnerを走らせると、Robolectricテストも実行しようとしてエラーになる

android-unit-test + Android Studio Unit Test
--------------------------------------------

Espresso 2.0の時代に追従すべく、日本時間で2月3日の早朝に大きなリニューアルコミットが[deckard-gradleにマージされました](https://github.com/robolectric/deckard-gradle/commit/b9c6549931059f470ee325a57c1286f677c04f50)。

### できるようになったこと

今回のリニューアルで、できるようになったことを列挙します。

* `src/androidTest`に置いたEspresso 2.0方式のテストを`./gradlew connectedAndroidTest`で実行できるようになった
* `src/test`に置いたRobolectricテストを`./gradlew test`で実行できるようになった
* Android Studio上でのandroidTestの実行
* 以下はAndroid Studio Unit Testプラグイン導入後に実行可能になる
    * **Android Studio上でのRobolectricテストの実行ができるようになった**
    * Android Studio上で`src/androidTest`と`src/test`が両方ともテストソースディレクトリとして認識される（緑色のアイコンになる）

大きな変更点としては下記２点が挙げられます。

* テストの種類ごとにディレクトリを分けても、それぞれにパスが通ったままテストコードを書けるようになった
* Android Studio上でRobolectricテストを実行できるようになった

### 実装の要点

android-unit-testによるテスト環境構築について、要点だけ解説します。

Espresso 2.0特有の設定内容（AndroidJUnitRunnerとか）については特に説明しません。[他の記事](http://qiita.com/AbeHaruhiko/items/91aa8fe0f2299969d0e7)をご覧ください。

#### 1. robolectric-gradle-pluginを置き換える

もうrobolectric-gradle-pluginは使いません。ありがとう。さようなら。

代わりに、[android-unit-testプラグイン](https://github.com/JCAndKSolutions/android-unit-test)を使います。

```gradle:build.gradle
classpath 'com.github.jcandksolutions.gradle:android-unit-test:2.1.1'
```

これに伴い、`apply plugin: 'robolectric'`や`robolectric { ... }`などのコードも削除します。これまでrobolectricブロックに書いていた設定を今後も扱いたい場合は、[android-unit-testのREADMEに類似の情報があります](https://github.com/JCAndKSolutions/android-unit-test/blob/master/README.md)ので、適宜設定してください。

#### 2. プラグインの適用とテスト用のdependencyを記述する

まずはandroid-unit-testプラグインを有効にします。必ず`android{ ... }`ブロックの **下に** 記述しましょう。

```gradle:build.gradle
apply plugin: 'com.android.application'

android {
  ...
}

apply plugin: 'android-unit-test'
```

さらにその下には、テスト用のdependencyを記述できます。Espressoテスト向けは`androidTestCompile`として、Robolectricテスト向けは`testCompile`として記述します。

下記は、deckard-gradleのbuild.gradleにおける一例です。

```gradle:build.gradle
apply plugin: 'android-unit-test'

dependencies {
    repositories {
        mavenCentral()
    }

    // Espresso
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.0')
    androidTestCompile('com.android.support.test:testing-support-lib:0.1')

    // Robolectric
    testCompile 'junit:junit:4.12'
    testCompile 'org.hamcrest:hamcrest-core:1.1'
    testCompile 'org.hamcrest:hamcrest-library:1.1'
    testCompile 'org.hamcrest:hamcrest-integration:1.1'

    testCompile('org.robolectric:robolectric:2.4') {
        exclude module: 'classworlds'
        exclude module: 'commons-logging'
        exclude module: 'httpclient'
        exclude module: 'maven-artifact'
        exclude module: 'maven-artifact-manager'
        exclude module: 'maven-error-diagnostics'
        exclude module: 'maven-model'
        exclude module: 'maven-project'
        exclude module: 'maven-settings'
        exclude module: 'plexus-container-default'
        exclude module: 'plexus-interpolation'
        exclude module: 'plexus-utils'
        exclude module: 'wagon-file'
        exclude module: 'wagon-http-lightweight'
        exclude module: 'wagon-provider-api'
    }
}
```

テスト内でmockitoなどを使いたい場合には、このタイミングで記述しておきましょう。

ひとまずこの段階で、`./gradlew connectedAndroidTest`と`./gradlew test`は実行できるようになっているはずです。お手軽ですね。

#### 3. Android Studioにプラグインを入れる

`Preferences > Plugins > Browse Repositories` で **Android Studio Unit Test** を探してインストールします。

![スクリーンショット 2015-02-04 2.00.17.png](https://qiita-image-store.s3.amazonaws.com/0/2276/48139789-38cd-264e-50ff-4c55d1fc9e77.png "スクリーンショット 2015-02-04 2.00.17.png")

これで準備は完了です。

Robolectricで使用していた `android.sourceSets.androidTest.setRoot("src/test")` が残っていると、上手くいかないので事前にbuild.gradleから削除しておきましょう。

`src/androidTest`と`src/test`が両方ともテストソースディレクトリとして扱われていることを確認できるはずです。

![スクリーンショット 2015-02-03 17.27.05.png](https://qiita-image-store.s3.amazonaws.com/0/2276/1149d5d6-fbe9-5f96-3138-0487db45f653.png "スクリーンショット 2015-02-03 17.27.05.png")


### Robolectricテストを動かしてみる

それでは、RobolectricのテストをAndroid Studioから実行してみましょう。どれかのテストクラスを右クリックして、JUnitテストとして実行します。（ドロイド君ではない方です）

![スクリーンショット 2015-02-04 2.10.55.png](https://qiita-image-store.s3.amazonaws.com/0/2276/09d796da-1a39-c2f8-7896-915bf29e16a8.png "スクリーンショット 2015-02-04 2.10.55.png")

すると、Android Studioのテスト結果画面が現れます。やはり、テストの成功が緑色のバーで見れると、達成感がありますね。

![スクリーンショット 2015-02-04 2.15.08.png](https://qiita-image-store.s3.amazonaws.com/0/2276/92c787f7-a94a-396a-af53-371997698fa5.png "スクリーンショット 2015-02-04 2.15.08.png")

まとめ
-----------

android-unit-testプラグインを使うことで、Android標準のテスティングフレームワークとRobolectricを、Android Studio内で編集・実行しやすい形で共存させることができるようになりました。

UIテストに強いEspressoと、ユニットテストに強いRobolectricを上手く使い分けながら、堅牢なアプリを開発するべく、ゴリゴリとテストを書いていけたら素晴らしいですね。

それでは皆様、良いテストライフを。

宣伝
------

[ウォーターセル株式会社](http://water-cell.jp)では、農業界に新しい情報プラットフォームを構築し、生産者に嬉しい情報の扱い方を模索しようとしています。この難題を一緒に考えてくれるAndroidアプリエンジニア、iOSアプリエンジニア、インフラエンジニア、Webフロントエンドエンジニア、Railsエンジニアを探しています。

興味のある方はTwitterで[@Nkzn](https://twitter.com/Nkzn)へ空リプか何かください。