2015.6.9追記:Android Studio 1.2 + Robolectric 3の組み合わせが良さそうなので、この記事をあまり鵜呑みにしないでください(参考)
Androiderの皆さん、テストしてますか?
今回は、UIテストが幸せになるEspresso 2.0と、ユニットテストが幸せになるRobolectricをAndroid Studio的に自然な形で併存させるための設定をご紹介します。
ゴールとしては、下記のような表示になります。
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
- 続・AndroidでJUnit4を使う方法(Android SDKで正式サポートされました!)
Robolectric
また、ユニットテストをモリモリ書いてきた諸兄は、Robolectricと仲良くしていらっしゃることと思います。JVM上で高速にテストを回すことができるRobolectricは、手元でのTDDにおいても、サーバー上のCIにおいても、欠かすことのできない存在になってきていますね。
問題点と解決策(旧)
どちらもDB層からUI層まで幅広い領域のテストに対応していますが、動作環境や実行速度の一長一短があるため、上手く使い分けができると幸せを享受しやすそうです。
しかし、両方を扱うには、少し問題がありました。Android Plugin for GradleやAndroid Studioは、テスト用ソースディレクトリを1つしか認識することができなかったのです。JUnit3しか書けなかった昔のandroidTestと、JUnit4でテストを書くRobolectricをプロジェクト内に併存させるのは、難しいものに思われました。
この問題への一案を提示したのが、robolectric/deckard-gradleというサンプルプロジェクトでした。
- src/test/
- com.example/
- espresso/
- FirstUITest
- SecondUITest
- FirstRobolectricTest
- SecondRobolectricTest
- espresso/
- com.example/
deckard-gradleでは、上記のようなパッケージ構造にした後、次のような設定を記述することで、実行されるテストの振り分けを行っていました。
robolectric {
include '**/*Test.class'
exclude '**/espresso/**/*.class'
}
これらの設定により、下記のテストが可能になっていました。
./gradlew connectedAndroidTest
./gradlew test
- Android Studio上でのandroidTestの実行
これはこれで良いものでしたが、少し不自然な対応だったために、1つの問題が残りました。
Espresso用のパッケージを別に用意したために、パッケージプライベートなフィールドに直接アクセスすることによる、テスト上の利便性を享受できなくなってしまったのです。
@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にマージされました。
できるようになったこと
今回のリニューアルで、できるようになったことを列挙します。
-
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
が両方ともテストソースディレクトリとして認識される(緑色のアイコンになる)
大きな変更点としては下記2点が挙げられます。
- テストの種類ごとにディレクトリを分けても、それぞれにパスが通ったままテストコードを書けるようになった
- Android Studio上でRobolectricテストを実行できるようになった
実装の要点
android-unit-testによるテスト環境構築について、要点だけ解説します。
Espresso 2.0特有の設定内容(AndroidJUnitRunnerとか)については特に説明しません。他の記事をご覧ください。
1. robolectric-gradle-pluginを置き換える
もうrobolectric-gradle-pluginは使いません。ありがとう。さようなら。
代わりに、android-unit-testプラグインを使います。
classpath 'com.github.jcandksolutions.gradle:android-unit-test:2.1.1'
これに伴い、apply plugin: 'robolectric'
やrobolectric { ... }
などのコードも削除します。これまでrobolectricブロックに書いていた設定を今後も扱いたい場合は、android-unit-testのREADMEに類似の情報がありますので、適宜設定してください。
2. プラグインの適用とテスト用のdependencyを記述する
まずはandroid-unit-testプラグインを有効にします。必ずandroid{ ... }
ブロックの 下に 記述しましょう。
apply plugin: 'com.android.application'
android {
...
}
apply plugin: 'android-unit-test'
さらにその下には、テスト用のdependencyを記述できます。Espressoテスト向けはandroidTestCompile
として、Robolectricテスト向けはtestCompile
として記述します。
下記は、deckard-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 を探してインストールします。
これで準備は完了です。
Robolectricで使用していた android.sourceSets.androidTest.setRoot("src/test")
が残っていると、上手くいかないので事前にbuild.gradleから削除しておきましょう。
src/androidTest
とsrc/test
が両方ともテストソースディレクトリとして扱われていることを確認できるはずです。
Robolectricテストを動かしてみる
それでは、RobolectricのテストをAndroid Studioから実行してみましょう。どれかのテストクラスを右クリックして、JUnitテストとして実行します。(ドロイド君ではない方です)
すると、Android Studioのテスト結果画面が現れます。やはり、テストの成功が緑色のバーで見れると、達成感がありますね。
まとめ
android-unit-testプラグインを使うことで、Android標準のテスティングフレームワークとRobolectricを、Android Studio内で編集・実行しやすい形で共存させることができるようになりました。
UIテストに強いEspressoと、ユニットテストに強いRobolectricを上手く使い分けながら、堅牢なアプリを開発するべく、ゴリゴリとテストを書いていけたら素晴らしいですね。
それでは皆様、良いテストライフを。
宣伝
ウォーターセル株式会社では、農業界に新しい情報プラットフォームを構築し、生産者に嬉しい情報の扱い方を模索しようとしています。この難題を一緒に考えてくれるAndroidアプリエンジニア、iOSアプリエンジニア、インフラエンジニア、Webフロントエンドエンジニア、Railsエンジニアを探しています。
興味のある方はTwitterで@Nkznへ空リプか何かください。