Robolectricで単体テスト実行中にBuild.VERSION.RELEASEからOSのバージョンを取得すると、"unknown"が返ってくる。
これでは都合が悪いので、テスト実行開始時にBuild.VERSION.RELEASEを書き換えてやろうと思ったのが事の発端。これが結構大変だった。。
やり方は以下。
- カスタムTestRunerを定義し、それを使用するようにする。
つまり、テストクラスに@RunWith(MyTestRunner.class)を付ける - カスタムTestRunnerは以下のように実装する。
public class MyTestRunner extends RobolectricTestRunner {
public MyTestRunner(Class<?> testClass) throws InitializationError {
super(testClass);
}
@Override
protected Class<? extends TestLifecycle> getTestLifecycleClass() {
return MyTestLifecycle.class;
}
public static class MyTestLifecycle extends DefaultTestLifecycle {
private static boolean setUpIsDone = false;
public MyTestLifecycle() throws Exception {
super();
if (!setUpIsDone) {
// この処理を呼ぶタイミングとしては一見beforeTestの方が適切に見えるが、
// beforeTestメソッドが呼ばれる前にBuild.VERSION.RELEASE等にShadowSystemPropertiesから
// 取得した文字列がセットされるためbeforeTestでは遅い。そのためここで呼んでいる。
setUpBeforeTestCasesOnlyOnce();
setUpIsDone = true;
}
}
/** テスト実行前に一度だけ実行する処理(テストケース毎に一度ではなく、テスト実行毎に一度実行する) */
private void setUpBeforeTestCasesOnlyOnce() throws Exception {
ShadowLog.stream = System.out;
// OSのバージョン、端末のモデル番号をShadowSystemPropertiesから取得できるようにする
Field field = ShadowSystemProperties.class.getDeclaredField("VALUES");
field.setAccessible(true);
Map<String, Object> values = (Map<String, Object>) field.get(null);
values.put("ro.build.version.release", "5.0.0");
values.put("ro.product.model", "Nexus 7");
MyUtils.logMethodCall(this);
}
}
}
setUpBeforeTestCasesOnlyOnceに相当するメソッドってTestLifecycleに用意されてないのかな。。ちょっと見た限りでは無さそうだった。 Robolectricのドキュメントは薄すぎる・・・わからないことがあったらRobolectricのテストコードを読むのが多分一番いい。
参考:
http://robolectric.blogspot.jp/2013/04/the-test-lifecycle-in-20.html http://www.ne.jp/asahi/hishidama/home/tech/java/reflection.html