Robolectric 3.0 から Robolectric 3.1 へのアップデートガイド

  • 21
    いいね
  • 0
    コメント

概要

つい先日、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();
  }
}
3.0時点でのテスト
@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に追加せよとあります。実際これでうまく動作するようになります。

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の場所もそのファイルの中で管理できるということで、全てのテストの共通する@Configrobolectric.propertiesに置き換え、不要になった独自拡張のテストランナーをRobolectricGradleTestRunnerに戻しました。

そうすると、コマンドの実行が3分ほどで終わるようになりました。

robolectric.propertiesは次のように記述します。

robolectric.properties
sdk=23
manifest=src/main/AndroidManifest.xml
packageName=com.example.debug
constants=com.example.BuildConfig
application=com.example.TestSampleApplication

パッケージ名やconstantsapplicationなどは適宜読み替えてください。
このファイルは$app_module/src/test/resources/robolectric.propertiesに配置します。

そもそも何故遅くなるのかと言う部分については、Robolectric 側のコミットログを見てみないことにはわからないため、まだ調査中です。ただ少なくとも、RobolectricGradleTestRunnerの拡張を@RunWithで指定すると、何らか実行速度に影響が出るようではあります。
なにか分かったことがあれば追記してみたいと思います。