PEEE802.11

モバイルソフトウェアエンジニアの備忘録

Android Studio 3.4/Gradle Plugin 5.1.1にアップデートしたらRobolectricのテストが失敗する

TL;DR

Robolectric 3.x(AndroidXにアップデート前のバージョン)を使っている状態でAndroid Studio 3.4にアップデートすると、Android Gradle Plugin 5.1.1+が強制されて、gradle.propertiesにandroid.enableUnitTestBinaryResources=falseを書かないとRobolectricを利用しているテストが初期化で失敗します。

問題発生条件

Robolectric 4.0+を使っている方はおそらく問題ないはず。

経緯

Robolectricは4.0+でAndroidXに対応している。が、Robolectric 4.0未満 + 従来のテストサポートライブラリでのテストコードがそれなりの量存在する場合、まだ移行できていないプロジェクトも多いのではないかと思う。 そんな状態でAndroid Studio 3.4にアップデートして、例えば以下のようなテストコードを実行すると、

@RunWith(RobolectricTestRunner::class)
@Config(
    constants = BuildConfig::class,
    sdk = [(Build.VERSION_CODES.LOLLIPOP)]
)
class ExampleUnitTest {
    @Test
    fun addition_isCorrect() {
        assertEquals(4, 2 + 2)
    }
}

テストランナー初期化時に NullPointerExceptionが発生してしまう。

java.lang.NullPointerException at org.robolectric.internal.DefaultManifestFactory.getFsFileFromPath(DefaultManifestFactory.java:65) at org.robolectric.internal.DefaultManifestFactory.identify(DefaultManifestFactory.java:24) at org.robolectric.RobolectricTestRunner.getAppManifest(RobolectricTestRunner.java:431) at org.robolectric.RobolectricTestRunner.getChildren(RobolectricTestRunner.java:255) at org.junit.runners.ParentRunner.getFilteredChildren(ParentRunner.java:426) at org.junit.runners.ParentRunner.getDescription(ParentRunner.java:351) at com.intellij.junit4.JUnit4IdeaTestRunner.getDescription(JUnit4IdeaTestRunner.java:78) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:50) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

例外が発生しているのはテストコードそのものではない。どうもマニフェストファイルを開こうとして、マニフェストファイルのパスの設定値自体が存在しないということが起こっている様子。 そんなこと言われても、もともとマニフェストのパスなんて自分ではどこにも指定していないのだが…

で、stackoverflowとか色々調べてみたが、@Configconstantsはdeprecatedだの、android { testOptions { unitTests { includeAndroidResources = true } } }が必要だの(設定済だった)、4.0ではandroid.enableUnitTestBinaryResources=trueが必要だの、なんとなく関係ありそうでなさそうな情報しか引っかからない。その中で気になったのが、android.enableUnitTestBinaryResourcesである。公式ページにはRobolectric 4.0+をAndroid Studio 3.3未満で使う場合はgradle.propertiesandroid.enableUnitTestBinaryResources=trueを書けよ!と書いてある。

robolectric.org

Add this line to your gradle.properties (no longer necessary with Android Studio 3.3+):

android.enableUnitTestBinaryResources=true

Android Studioでプロジェクトを作った時に生成されるgradle.propertiesにこのフラグは存在しないが、試しにこのフラグを明示的にfalseにする行を追加してみると、テストが通るようになった。

以下のパスにテスト用のコンフィグが生成されているのだが、このフラグのtrue/falseでコンフィグが変わることを確認した。何も設定しなければデフォルトでtrueの設定値が生成された。

your/module/path/build/intermediates/unit_test_config_directory/debugUnitTest/generateDebugUnitTestConfig/out/com/android/tools/test_config.properties

android.enableUnitTestBinaryResources=false

## Generated by the Android Gradle Plugin
#Wed May 01 19:01:33 JST 2019
android_sdk_home=~/Library/Android/sdk/platforms/android-28
android_custom_package=com.example.your.app.id
android_merged_assets=your/module/path/build/intermediates/merged_assets/debug/out
android_merged_manifest=your/module/path/build/intermediates/merged_manifests/debug/AndroidManifest.xml
android_merged_resources=your/module/path/build/intermediates/merged-not-compiled-resources/debug

android.enableUnitTestBinaryResources=true or 設定を書かない

## Generated by the Android Gradle Plugin
#Wed May 01 18:46:52 JST 2019
android_sdk_home=~/Library/Android/sdk/platforms/android-28
android_custom_package=com.example.your.app.id
android_merged_assets=your/module/path/build/intermediates/merged_assets/debug/out
android_merged_manifest=your/module/path/build/intermediates/merged_manifests/debug/AndroidManifest.xml
android_resource_apk=your/module/path/build/intermediates/apk_for_local_test/debugUnitTest/packageDebugUnitTestForUnitTest/apk-for-local-test.ap_

最後の行だけが違う。フラグの名前の通り、APKにパッケージされる前の中間生成物であるmerged resourceを利用するか、APKをまるっと利用するか(binary resources)を切り替えるようだ。

色々な現場で割とぶち当たりそうな問題に思えるのだが、あまりみんな騒いでないので、みんなAndroid Studio 3.4にまだ上げてないのか、Robolectric使ってないのか、すでにAndroidXに移行済みなのか…