Robolectricを使ったライブラリプロジェクトのビルドでテストが失敗する
ライブラリプロジェクトの時だけ失敗する。
プロジェクトルートと、モジュールルートのbuild.gradleは次のようになっています。
build.gradle
buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:0.12.+' classpath 'org.robolectric:robolectric-gradle-plugin:0.11.+' } } allprojects { repositories { jcenter() } }
${module}/build.gradle
apply plugin: 'com.android.library' apply plugin: 'robolectric' android { compileSdkVersion 16 buildToolsVersion "19.1.0" defaultConfig { applicationId "com.sakebook.android.dialoghelper" minSdkVersion 8 targetSdkVersion 16 } buildTypes { release { runProguard false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } } sourceSets { androidTest { setRoot('src/test') } } } robolectric { include "**/*Test.class" exclude "**/espresso/*.class" } dependencies { compile 'com.android.support:support-v4:19.1+' androidTestCompile 'junit:junit:4.11' androidTestCompile 'com.squareup:fest-android:1.0.8@aar' androidTestCompile('org.robolectric:robolectric:2.3') } apply from: 'https://raw.github.com/chrisbanes/gradle-mvn-push/master/gradle-mvn-push.gradle'
適当なテストコードを書いて、./gradlew clean test
タスクを実行すると、下記のようなエラーがでます。
java.lang.RuntimeException: java.lang.NullPointerException at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:240) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222) at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:177) at org.junit.runners.ParentRunner.run(ParentRunner.java:300) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:86) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:49) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:69) at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:48) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35) at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) at org.gradle.messaging.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32) at org.gradle.messaging.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93) at com.sun.proxy.$Proxy2.processTestClass(Unknown Source) at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:105) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35) at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) at org.gradle.messaging.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:355) at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:64) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918) at java.lang.Thread.run(Thread.java:695) Caused by: java.lang.NullPointerException at org.robolectric.AndroidManifest.initMetaData(AndroidManifest.java:364) at org.robolectric.res.builder.RobolectricPackageManager.addManifest(RobolectricPackageManager.java:364) at org.robolectric.internal.ParallelUniverse.setUpApplicationState(ParallelUniverse.java:77) at org.robolectric.RobolectricTestRunner.setUpApplicationState(RobolectricTestRunner.java:430) at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:236) ... 35 more
AndroidManifest.class周りでエラーを吐いてるようなので、
apply plugin: 'com.android.library'
apply plugin: 'android'
とそれぞれ実行したときのAndroidManifest.xmlを比べてみました。
ビルド時のAndroidManifest.xmlは以下のようになっています。
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.sakebook.android.dialoghelper" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="16" /> </manifest>
apply plugin: 'com.android.library' の場合
${module}/build/intermediates/bundles/
に出力されます。
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.sakebook.android.dialoghelper" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="16" /> </manifest>
apply plugin: 'android' の場合
${module}/build/intermediates/manifests/debug/
に出力されます。
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.sakebook.android.dialoghelper" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="16" /> <application /> </manifest>
<application />
が追加される
本来は必須なのですが、ライブラリプロジェクトの場合は、親プロジェクトにAndroidManifest.xmlが必ずあるため、無くても動きます。
しかし、親プロジェクトの場合は必須なので、pluginで自動で挿入してくれるようです。これが無いと、Robolectricでエラーを吐くため、ライブラリプロジェクトに追加してあげましょう。
変更後のAndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.sakebook.android.dialoghelper" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="16" /> <application /> </manifest>
apply plugin: 'android'
の場合と同じにしただけですが、これで無事エラーを起こさないため、テストができます。
まとめ
- ライブラリプロジェクトでもちゃんと
<application />
を書きましょう。