Table of contents:
- 概要
- Robolectric Instrumentationを導入する
- testInstrumentationRunnerを設定する
- robolectric.propertiesを導入する
- src/test を src/androidTest にシンボリックリンクする
- テストをAndroid Instrumentation Testing API準拠にする
- FAQ
概要
テストクラスをAndroid Instrumentation TestsとRobolectric Tests両方で走らせる - Islands in the byte stream のハックをライブラリ化しました。
gfx/Robolectric-Instrumentation - Java - GitHub
Ormaのほうはこれに書き換え済みです。またすでにRoblectricを導入していたAndroidアプリをRobolectric-Instrumentationに移行する実例は以下から。
さて、これはRobolectricでAndroid InstrumentationのAPIのサブセットを実装することにより、同じテストコードを両方のテストフレームワークで走らせられるようにするものです。なおEspressoを使うテストは移植できません*1。
新規での導入手順は以下のとおりです。 dependenciesを除けば example project が完成形です。
Robolectric Instrumentationを導入する
dependenciesに書くだけです。他にも必要なライブラリがあれば書いてください。
dependencies {
testCompile 'com.github.gfx.android.robolectricinstrumentation:robolectric-instrumentation:3.0.2'
testCompile 'junit:junit:4.12'
}
testInstrumentationRunnerを設定する
これは androidTest でJUnit4を走らせるために必要な設定です。Robolectric-InstrumentationはJUnit4にしか対応していません。
android {
defaultConfig {
testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
}
}
robolectric.propertiesを導入する
サブプロジェクトで app があるとすれば、 app/src/test/resources/roblectric.properties に設定ファイルを置きます。これはRobolectric全体の設定に使うファイルで、Robolectric-Instrumentationを使う場合はテストクラスではRobolectric APIを使えないのでこの設定ファイルを使う必要があります。またRobolectric-Instrumentation でも project という設定が追加で必要です。
最低限必要な設定は以下のとおりです。
# サブプロジェクトの名前 project=app # RobolectricでシミュレートするAPI level sdk=16
strings.xml などのリソースにアクセスしたいときは更に BuildConfig の設定をする必要があります。
constants=com.example.app.BuildConfig
src/test を src/androidTest にシンボリックリンクする
(cd app/src && ln -s test androidTest)
テストをAndroid Instrumentation Testing API準拠にする
Robolectric-Instrumentationの実体は com.android.support.test:runner の互換実装です。具体的には、 AndroidJUnit4 と InstrumentationRegistry です。Robolectric APIではなくこれらを使うように書き換えます。
+ @RunWith(AndroidJUnit4.class) - @RunWith(RobolectricGradleTestRunner.class) - @Config(constants = BuildConfig.class, sdk = 16)
+ Context context = InstrumentationRegistry.getTargetContext(); - Context context = RuntimeEnvironment.application;
これで、 ./gradlew test と ./gradlew androidTest 両方で動くようになるはずです。
ただEspressoなどviewを含めたテストはできませんし、Handlerまわりを使っていると動かないなどのRobolectric特有の現象を考慮する必要があります。Ormaでは特に問題なく移植できましたが、これはほとんどSQLite関連のAPIしか使っていないからです。
FAQ
Unit Tests を走らせると Stub! となって落ちる / Android Instrumentation Tests を走らせると no tests になる
Android Studio 1.5の時点では、同じプロジェクト(モジュール)で Unit Tests と Android Instrumentation Tests を切り替えるとAndroid Studioがテスト設定をうまく認識できなくなるようです。Edit Configurationsで起動設定を一度消して作りなおすとうまく行きますが、面倒なのでただ走らせるだけならコマンドラインから実行するのが無難です。
Espressoと併用したい / Android Instrumentation Testsのときだけ走るテストを作りたい
実行環境を判定するヘルパメソッドを作って assumeThat() で条件付きスキップするといいと思います。
Robolectric-Instrumentation/.../ActivityTest.java
package com.github.gfx.android.robolectricinstrumentation; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import android.app.Activity; import android.support.test.InstrumentationRegistry; import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.action.ViewActions.click; import static android.support.test.espresso.matcher.ViewMatchers.withId; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import static org.junit.Assume.assumeTrue; @RunWith(AndroidJUnit4.class) public class ActivityTest { boolean runOnAndroid() { return System.getProperty("java.vm.name").equals("Dalvik"); } boolean runOnRobolectric() { return !runOnAndroid(); } @Rule public ActivityTestRule<MainActivity> mainActivityRule = new ActivityTestRule<>(MainActivity.class); @Test public void testPerformClick() throws Exception { assumeTrue("Robolectric does not support Espresso", runOnAndroid()); onView(withId(R.id.button)) .perform(click()); } }
*1:InstrumentationRegistry経由でActivityを起動することくらいならできますが