概要
つい先日、Robolectric 3.1がリリースされました。Robolectric 3.0がリリースされたのは2015年7月で、Robolectric 3.1のリリースは2016年6月ですから、ほぼ1年ぶりのアップデートとなりました。
主なアップデート内容はLollipop-MR1およびMarshmallowサポート、Play ServicesのShadowクラスの改善などがあるようです。
公式のアップデートガイドによると、ShadowApplication
クラスの機能がRuntimeEnvironment.application
に置き換えられるようですが、実際にはこれ以外にも多くの変更が入っており、おそらくこのガイドにある変更だけでは不足することが出てくるでしょう(3.0から3.1へのdiffを出してみましたが、GitHub上では大きすぎて一部のdiffしか閲覧できません😢)。
この記事には、アップデートに際して公式ガイドにはない部分で修正が必要な箇所について記載しています。
sdk = 23
に伴う修正
これによって、MarshmallowのAPIに依存する部分のテストも出来るようになります。Robolectric 3.0まではLollipopが最新のサポートバージョンでしたので、API Levelをチェックするif文があるメソッドをテストしている場合、いくらかの変更が必要になります。
public class TestTarget {
private Something something;
public TestTargeT(Something something) {
this.something = something;
}
public boolean hoge() {
if (Build.VERSION.SDK_IN >= Build.VERSION_CODES.LOLLIPOP_MR1) {
return something.checkHogeApi22();
}
return something.checkHoge();
}
}
@Config(constants = BuildConfig.class, sdk = 21)
@RunWith(RobolectricGradleTestRunner.class)
public class HogeTest {
private TestTarget target;
private Something mockSomething;
@Before
public void setUp() throws Exception {
mockSomething = // mockito とかでモックする;
target = new TestTarget(mockSomething);
}
@Test
public void hoge_fallback() throws Exception {
target.hoge();
// mockito とかで Something#checkHoge が呼ばれたことを確認する等
}
}
3.0時点でのテストではsdk = 21
ですので、hogeメソッドのif
の条件文はfalse
になりそのままcheckHoge()
メソッドが呼ばれます。これを3.1にアップデートしてsdk = 23
に修正すると、呼ばれるのはcheckHogeApi22()
メソッドになるので、テストが失敗するようになります。
NoClassDefFoundError: javax/microedition/khronos/opengles/GL
対応
おそらくRobolectric 3.0に上げた時点でもプロジェクトによっては発生していたかもしれませんが、自分のプロジェクトでは3.1に上げたところで発生しました。
Viewがテストに絡んでくると起こる問題で、Robolectricがjavax配下のパッケージ群をまるまるロードしないようにしていることが原因のようです。Robolectricのイシューには、以下のように依存関係をbuild.gradle
に追加せよとあります。実際これでうまく動作するようになります。
dependencies {
// ...
testCompile 'org.khronos:opengl-api:gl1.1-android-2.1_r1'
}
テストが遅い
テストの実行が目に見えて遅くなります。弊社のテストケースは750ほどありますが、単純にテストを実行するだけのコマンドを叩いた時に、3.0まではおよそ2分ほどで完了していたものが、3.1で6〜7分ほどかかるようになってしまいました。
$ ./gradlew testDebugUnitTest
提出されたプルリクエストによると、ShadowWrangler#methodInvoked()
の処理でキャッシュをコレクションから読み出す部分に問題があるようですが、どうもそれだけが問題では無いようです。
弊社の場合、以前AndroidManifest.xml
がテストランナーから見えない問題の解消のために、独自にRobolectricGradleTestRunner
を拡張していました。
最近になって、@Config
に指定する内容はrobolectric.properties
というファイルに書き出せることを知り、かつAndroidManifest.xml
の場所もそのファイルの中で管理できるということで、全てのテストの共通する@Config
をrobolectric.properties
に置き換え、不要になった独自拡張のテストランナーをRobolectricGradleTestRunner
に戻しました。
そうすると、コマンドの実行が3分ほどで終わるようになりました。
robolectric.properties
は次のように記述します。
sdk=23
manifest=src/main/AndroidManifest.xml
packageName=com.example.debug
constants=com.example.BuildConfig
application=com.example.TestSampleApplication
パッケージ名やconstants
、application
などは適宜読み替えてください。
このファイルは$app_module/src/test/resources/robolectric.properties
に配置します。
そもそも何故遅くなるのかと言う部分については、Robolectric 側のコミットログを見てみないことにはわからないため、まだ調査中です。ただ少なくとも、RobolectricGradleTestRunner
の拡張を@RunWith
で指定すると、何らか実行速度に影響が出るようではあります。
なにか分かったことがあれば追記してみたいと思います。