Robolectricの@Config
アノテーションで指定する各種設定を
共通化させる方法を紹介します。
(この内容はAndroid Testing Bootcamp #2で、発表した内容と同じですが、
そこで説明しきれなかった補足も付け加えています。)
前提条件
この記事は、Robolectric3.1を使用した際の説明となっています。
ただしRobolectric3.0でも、少しコードを修正すれば適用できる内容となります。
このサンプルプロジェクトで、3.1と3.0それぞれに対応したコードを載せています。
一般的なRobolectricの使い方
一般的なRobolectircの使い方は以下のとおりだと思います。
-
@RunWith
にRobolectricGradleTestRunner
を指定 -
@Config
に必要な各種設定を指定
@RunWith(RobolectricGradleTestRunner.class)
@Config(
sdk = 23,
constants = BuildConfig.class
)
public class ExampleUnitTest {
@Test public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
問題点
ただし上記のような、テストクラスで@Config
の各設定を指定するという、一般的な方法を用いると
以下の様な問題が後々起こる可能性が出てきます。
- テストクラスを作成するたびに
@Config
を指定しなくてはならず面倒 - 指定したすべての
@Config
の設定を一括で変更したい場合、
すべてのテストクラスを修正しなくてはいけない - Robolectricがバージョンアップし、その結果
@Config
の設定方法が変更になった場合、
すべてのテストクラスを修正しなくてはいけない
以上の問題から、@Config
の設定を共通化できるものは、できるだけしておきたいです。
共通化する2つの方法
以上を踏まえ、@Config
の設定を共通化する方法を、2つ紹介します。
その1 - ファイルに切り出す
この方法は、公式で提供している機能を使います。
やり方としては、以下のとおりです。
1. robolectric.propertiesというファイルを作る
2. このファイルに、共通化させたい@Config
の設定を記述する
3. このファイルを、app/src/test/resources
ディレクトリに置く
sdk=23
constants=my.package.BuildConfig
結果
このテストランナーを用いたテストコードは以下になります。
これにより、@Config
アノテーションを指定せずに、共通化したい@Config
の設定を指定できます。
@RunWith(RobolectricGradleTestRunner.class)
public class ExampleUnitTest {
@Test public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
その2 - テストランナーを作成する
この方法では、@Config
の設定を共通化できるテストランナーを作ります。
public class RobolectricCustomRunner extends RobolectricGradleTestRunner {
private static final int[] SDK = new int[]{23};
public RobolectricCustomRunner(Class<?> klass) throws InitializationError {
super(klass);
}
@Override public Config getConfig(Method method) {
// 実際のテストクラスで指定、生成されたConfigを取得
Config c = super.getConfig(method);
// Configの設定を書き換えたものを登録する
return new Config.Implementation(
sdkLevel,
c.manifest(), c.qualifiers(), c.packageName(),
c.abiSplit(), c.resourceDir(), c.assetDir(),
c.buildDir(), c.shadows(), c.instrumentedPackages(),
c.application(), c.libraries(),
constants
);
}
// Config#sdkを指定していなかった場合は23に
private static int[] pickSdkLevel(int[] sdkArray) {
return sdkArray.length == 0 ? SDK : sdkArray;
}
private static Class<?> pickConstants(Class<?> constants) {
return constants == Void.class ? BuildConfig.class : constants;
}
}
結果
このテストランナーを用いたテストコードは以下になります。
これにより、@Config
アノテーションを指定せずに、共通化したい@Config
の設定を指定できます。
@RunWith(RobolectricCustomRunner.class)
public class ExampleUnitTest {
@Test public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}
どちらの方法を採用するべきか
これは私個人の意見となりますが、以下の理由により、
テストランナーを用いた方法を採用したほうがいいと考えています。
- コードで設定を記述できるので、柔軟に指定できる。
(例えば各BuildVariantで違う設定をする) - 今後テストランナーを使ってRobolectricのカスタマイズをする必要が出てきた時、
その設定と@Config
の設定を一元管理できる - ファイル、テストランナー、どちらの方法でもテストクラス作成時のコード量は変わらない。
(どちらも@RunWith
でテストランナーを指定する)
補足
テストクラスに@Config
を絶対に書かないほうが良いというわけではなく、
Robolectricを使うテストクラスで常に指定する@Config
の設定を出来る限り共通化したほうがいいのではないかというのが、
この記事の伝えたいことになります。