Using multidex + "dexopener" - java.lang.NoClassDefFoundError
jaredsburrows opened this issue · 14 comments
Reposted from: mockito/mockito#1082 (comment)
Starting 0 tests on emulator-5554 - 4.4.2
Tests on emulator-5554 - 4.4.2 failed: Instrumentation run failed due to 'java.lang.NoClassDefFoundError'
com.android.builder.testing.ConnectedDevice > No tests found.[emulator-5554 - 4.4.2] FAILED
No tests found. This usually means that your test classes are not in the form that your test runner expects (e.g. don't inherit from TestCase or lack @Test annotations).
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':connectedDebugAndroidTest'.
> There were failing tests. See the report at: file:///Users/<>/repo/android-gif-example/build/reports/androidTests/connected/index.html
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED in 6s
94 actionable tasks: 39 executed, 55 up-to-date
Version of dexmaker - 0.10.1
I was using my example project here: https://github.com/jaredsburrows/android-gif-example
Your project seems to use neither Multidex nor DexOpener.
Could you tell me the steps to reproduce?
I just applied multidex and your library along with mockito-android
library.
Are you using Multidex for your test apk?
If so, AndroidJUnitRunner does not support mulitdex for test apks, so DexOpener does not support it.
https://developer.android.com/studio/build/multidex.html?hl=en#testing
Notes:
- Don't use MultiDexTestRunner, which is deprecated; use AndroidJUnitRunner instead.
- Using multidex to create a test APK is not currently supported.
Yes, I use the AndroidJUnitRunner.
Multidex 1.0.2 is out:
https://developer.android.com/topic/libraries/support-library/revisions.html#25-4-0
Concurrent with this support library release, we are also releasing multidex version 1.0.2. This version includes the following important changes:
Allows multidexing of instrumentation APK.
Deprecates MultiDexTestRunner (AndroidJUnitRunner should be used instead).
Provides better protection against some bad archive extraction management of the app.
Fixes a bug that could lead to abandoned temporary files.
Provides faster installation when done in concurrent process.
Fixes an installation bug on API 19 and 20.
https://developer.android.com/topic/libraries/support-library/revisions.html#25-4-0
In theory multidex for the test APK should work. But the fact of the matter is using your library + mockito-android
makes it multidex anyways.
Thank you for your information.
First I tried Multidex 1.0.2 without using DexOpener, but your instrumented tests does not seem to work.
$ ./gradlew clean connectedDebugAndroidTest
[snip]
Starting 0 tests on Nexus_5_API_19(AVD) - 4.4.2
Tests on Nexus_5_API_19(AVD) - 4.4.2 failed: Instrumentation run failed due to 'java.lang.NoClassDefFoundError'
com.android.builder.testing.ConnectedDevice > No tests found.[Nexus_5_API_19(AVD) - 4.4.2] FAILED
No tests found. This usually means that your test classes are not in the form that your test runner expects (e.g. don't inherit from TestCase or lack @Test annotations).
FAILURE: Build failed with an exception.
Logcat is here:
E/AndroidRuntime( 3574): FATAL EXCEPTION: Instr: android.support.test.runner.AndroidJUnitRunner
E/AndroidRuntime( 3574): Process: burrows.apps.example.gif.debug, PID: 3574
E/AndroidRuntime( 3574): java.lang.NoClassDefFoundError: org.junit.runner.manipulation.Filter$1
E/AndroidRuntime( 3574): at org.junit.runner.manipulation.Filter.<clinit>(Filter.java:21)
E/AndroidRuntime( 3574): at android.support.test.internal.runner.TestRequestBuilder.<init>(TestRequestBuilder.java:83)
E/AndroidRuntime( 3574): at android.support.test.internal.runner.TestRequestBuilder.<init>(TestRequestBuilder.java:525)
E/AndroidRuntime( 3574): at android.support.test.runner.AndroidJUnitRunner.createTestRequestBuilder(AndroidJUnitRunner.java:370)
E/AndroidRuntime( 3574): at android.support.test.runner.AndroidJUnitRunner.buildRequest(AndroidJUnitRunner.java:343)
E/AndroidRuntime( 3574): at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:260)
E/AndroidRuntime( 3574): at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1701)
W/ActivityManager( 1568): Error in app burrows.apps.example.gif.debug running instrumentation ComponentInfo{burrows.apps.example.gif.test/android.support.test.runner.AndroidJUnitRunner}:
W/ActivityManager( 1568): java.lang.NoClassDefFoundError
W/ActivityManager( 1568): java.lang.NoClassDefFoundError: org.junit.runner.manipulation.Filter$1
I/ActivityManager( 1568): Force stopping burrows.apps.example.gif.debug appid=10067 user=0: finished inst
I/ActivityManager( 1568): Killing 3574:burrows.apps.example.gif.debug/u0a67 (adj 0): stop burrows.apps.example.gif.debug
I/ActivityManager( 1568): Delay finish: com.google.android.gms/.stats.service.DropBoxEntryAddedReceiver
I/ActivityManager( 1568): Resuming delayed broadcast
Could you please point out any problems with the following changes?
diff --git a/build.gradle b/build.gradle
index c45e8e0..94567bb 100644
--- a/build.gradle
+++ b/build.gradle
@@ -55,6 +55,7 @@ android {
versionName "1.0"
minSdkVersion rootProject.ext.lollipop ? 21 : rootProject.ext.minSdkVersion // Optimize build speed - build with minSdk 21 if using multidex
targetSdkVersion rootProject.ext.targetSdkVersion
+ multiDexEnabled true
testApplicationId "burrows.apps.example.gif.test"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
resConfigs "en" // Optimize APK size - keep only english resource files for now
@@ -158,9 +159,11 @@ android {
configurations.all {
resolutionStrategy.force deps.supportAnnotations
resolutionStrategy.force deps.kotlinStdlib
+ resolutionStrategy.force 'com.android.support:multidex:1.0.2'
}
dependencies {
+ compile 'com.android.support:multidex:1.0.2'
// Android/Google
compile deps.design
compile deps.cardviewv7
@@ -187,8 +190,9 @@ dependencies {
androidTestCompile deps.junit
androidTestCompile deps.assertjCore
androidTestCompile deps.mockitoKotlin, { exclude group: "net.bytebuddy" } // DexMaker has it"s own MockMaker
- androidTestCompile deps.mockitoCore, { exclude group: "net.bytebuddy" } // DexMaker has it"s own MockMaker
- androidTestCompile deps.dexmakerMockito, { exclude group: "net.bytebuddy" } // DexMaker has it"s own MockMaker
+// androidTestCompile deps.mockitoCore, { exclude group: "net.bytebuddy" } // DexMaker has it"s own MockMaker
+// androidTestCompile deps.dexmakerMockito, { exclude group: "net.bytebuddy" } // DexMaker has it"s own MockMaker
+ androidTestCompile 'org.mockito:mockito-android:2.8.47'
androidTestCompile deps.runner
androidTestCompile deps.espressoCore
androidTestCompile deps.espressoIntents
diff --git a/src/main/kotlin/burrows/apps/example/gif/App.kt b/src/main/kotlin/burrows/apps/example/gif/App.kt
index ae5c2ea..baed7f9 100644
--- a/src/main/kotlin/burrows/apps/example/gif/App.kt
+++ b/src/main/kotlin/burrows/apps/example/gif/App.kt
@@ -1,7 +1,7 @@
package burrows.apps.example.gif
import android.annotation.SuppressLint
-import android.app.Application
+import android.support.multidex.MultiDexApplication
import burrows.apps.example.gif.presentation.di.component.ActivityComponent
import burrows.apps.example.gif.presentation.di.component.AppComponent
@@ -9,7 +9,7 @@ import burrows.apps.example.gif.presentation.di.component.AppComponent
* @author [Jared Burrows](mailto:jaredsburrows@gmail.com)
*/
@SuppressLint("Registered")
-open class App : Application() {
+open class App : MultiDexApplication() {
lateinit var appComponent: AppComponent
lateinit var activityComponent: ActivityComponent
Perhaps I found a solution.
- Create your own JUnitRunner as follows
class JUnitRunner : AndroidJUnitRunner() {
override fun onCreate(arguments: Bundle?) {
MultiDex.installInstrumentation(context, targetContext)
super.onCreate(arguments)
}
}
- Set JUnitRunner as the default test instrumentation runner
android {
defaultConfig {
// testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
testInstrumentationRunner "your.own.JUnitRunner"
}
}
If you use DexOpener, change the base class of your own JUnitRunner to DexOpenerAndroidJUnitRunner
.
class JUnitRunner : DexOpenerAndroidJUnitRunner() {
override fun onCreate(arguments: Bundle?) {
MultiDex.installInstrumentation(context, targetContext)
super.onCreate(arguments)
}
}
But now, due to the issue #8, DexOpener does not work with your project.
As a workaround, add DataBindingUtil::class.java
into App#onCreate()
.
open class App : Application() {
lateinit var appComponent: AppComponent
lateinit var activityComponent: ActivityComponent
override fun onCreate() {
super.onCreate()
DataBindingUtil::class.java // Add this line
// Setup components
appComponent = AppComponent.init(this)
activityComponent = ActivityComponent.init(appComponent)
}
}
@tmurakami Thank you so much! I will try it in my project soon!
@tmurakami If you do not mind me asking, how did you know about installInstrumentation
? I do not see it in the documentation: https://developer.android.com/reference/android/support/multidex/MultiDex.html.
I had a mistake.
I overlooked that your app uses applicationIdSuffix
.
There is a problem with the version 0.10.2, so you should use 0.10.1.
DexOpenerAndroidJUnitRunner does not work well with applicationIdSuffix
.
Instead you should use DexOpener#builder(Context)
on your JUnitRunner.
class JUnitRunner : AndroidJUnitRunner() {
override fun newApplication(cl: ClassLoader, className: String?, context: Context): Application {
DexOpener.builder(context)
.openIf { it.startsWith("burrows.apps.example.gif") }
.build()
.installTo(cl)
return super.newApplication(cl, className, context)
}
}
@tmurakami Thanks for sharing the source. I guess the documentation is not up yet.
Thanks, I will look into it.
This issue seems to be solved, so I will close this.
If there is any problem, please reopen this issue.
Thank you.