Robolectric AndroidX fragments NoClassDefFoundError - android

After migration of code and tests to AndroidX, all seems to work pretty well, however Robolectric junit tests for two fragments are failing due to NoClassDefFoundError: androidx/fragment/testing/R$style exception.
The stack trace:
java.lang.NoClassDefFoundError: androidx/fragment/testing/R$style at
androidx.fragment.app.testing.FragmentScenario$EmptyFragmentActivity.onCreate(FragmentScenario.java:79)
at android.app.Activity.performCreate(Activity.java:5933) at
android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1105)
at
androidx.test.runner.MonitoringInstrumentation.callActivityOnCreate(MonitoringInstrumentation.java:674)
at
org.robolectric.android.controller.ActivityController.lambda$create$0(ActivityController.java:69)
at
org.robolectric.shadows.ShadowLooper.runPaused(ShadowLooper.java:365)
at
org.robolectric.android.controller.ActivityController.create(ActivityController.java:69)
at
org.robolectric.android.controller.ActivityController.create(ActivityController.java:74)
at
org.robolectric.android.internal.LocalActivityInvoker.startActivity(LocalActivityInvoker.java:39)
at
androidx.test.core.app.ActivityScenario.launch(ActivityScenario.java:207)
at
androidx.fragment.app.testing.FragmentScenario.internalLaunch(FragmentScenario.java:283)
at
androidx.fragment.app.testing.FragmentScenario.launchInContainer(FragmentScenario.java:265)
For testing fragments I'm using FragmentScenario, and it seems the FragmentScenario.EmptyFragmentActivity refers to a missing R class from a package androidx.fragment.testing.R:
setTheme(getIntent().getIntExtra(THEME_EXTRAS_BUNDLE_KEY,
R.style.FragmentScenarioEmptyFragmentActivityTheme));
Any idea what might be wrong? Maybe I'm missing a dependency that's not that obvious to me.
Reproducible in following project:
https://github.com/marcinbak/androidx-test-error
Also reported in Google's issue tracker: https://issuetracker.google.com/issues/122321150

According to answer from Google it is required to include androidx_fragment_test (androidx.fragment:fragment-testing:1.1.0-alpha03) artifact in the testing APK (not in instrumentation APK or testing dependency).
What this means it has to be added as debugImplementation androix_fragment_test in your build.gradle file.
If you also run tests on release target then you have to add implementation androix_fragment_test.
You can see the whole conversation in Google's issue tracker: https://issuetracker.google.com/issues/122321150

Related

Crash on Google Play Pre-Launch Report: java.lang.NoSuchMethodError

Getting some very odd behaviour on my Google Play Pre-Launch report, and it's only just started occurring, but I can't pin it down to anything I have changed. I also can't reproduce the issue myself.
Every test device seems to be failing in the pre-launch report (6/6 devices all fail the same way). However repeating the steps on the same build on a real device, I can never recreate it. The stack trace is as follows:
FATAL EXCEPTION: Thread-8
Process: uk.myapp.test, PID: 25312
java.lang.NoSuchMethodError: No static method createWithResource(Landroid/content/res/Resources;Ljava/lang/String;I)Landroidx/core/graphics/drawable/IconCompat; in class Landroidx/core/graphics/drawable/IconCompat; or its super classes (declaration of 'androidx.core.graphics.drawable.IconCompat' appears in base.apk)
at androidx.core.app.NotificationCompat$Action.<init>(NotificationCompat.java:4572)
at com.google.android.exoplayer2.ui.PlayerNotificationManager.createPlaybackActions(PlayerNotificationManager.java:1439)
at com.google.android.exoplayer2.ui.PlayerNotificationManager.<init>(PlayerNotificationManager.java:757)
at com.google.android.exoplayer2.ui.PlayerNotificationManager$Builder.build(PlayerNotificationManager.java:564)
at uk.myapp.test.AudioPlayerService.onCreate(AudioPlayerService.java:96)
at android.app.ActivityThread.handleCreateService(ActivityThread.java:3570)
at android.app.ActivityThread.access$1300(ActivityThread.java:200)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1672)
at android.os.Handler.dispatchMessage(Handler.java:106)
at androidx.test.espresso.base.Interrogator.loopAndInterrogate(Interrogator.java:10)
at androidx.test.espresso.base.UiControllerImpl.loopUntil(UiControllerImpl.java:7)
at androidx.test.espresso.base.UiControllerImpl.loopUntil(UiControllerImpl.java:1)
at androidx.test.espresso.base.UiControllerImpl.injectMotionEvent(UiControllerImpl.java:5)
at androidx.test.espresso.action.MotionEvents.sendUp(MotionEvents.java:6)
at androidx.test.espresso.action.MotionEvents.sendUp(MotionEvents.java:1)
at androidx.test.espresso.action.Tap.sendSingleTap(Tap.java:5)
at androidx.test.espresso.action.Tap.-$$Nest$smsendSingleTap(Unknown Source:0)
at androidx.test.espresso.action.Tap$1.sendTap(Tap.java:1)
at androidx.test.espresso.action.GeneralClickAction.perform(GeneralClickAction.java:4)
at androidx.test.espresso.ViewInteraction$SingleExecutionViewAction.perform(ViewInteraction.java:2)
at androidx.test.espresso.ViewInteraction.doPerform(ViewInteraction.java:21)
at androidx.test.espresso.ViewInteraction.-$$Nest$mdoPerform(Unknown Source:0)
at androidx.test.espresso.ViewInteraction$1.call(ViewInteraction.java:2)
at androidx.test.espresso.ViewInteraction$1.call(ViewInteraction.java:1)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6718)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Not exactly sure but I think it's an issue with the Robo tests that Firebase test lab uses. I haven't tried using their instrumented tests instead, but it's the only explanation I can currently think of.
I implemented reverting the Firebase BOM version like the other answer suggested, but it didn't (or at least didn't fully resolve) the issue for me. Reverting this other dependency finished solving it for me:
Crashing version:
implementation 'com.google.android.gms:play-services-location:19.0.1'
Working version:
implementation 'com.google.android.gms:play-services-location:18.0.0'
EDIT: initially reverting to 29.0.2 release of Google Firebase BOM (from 29.0.3) fixed the issue for me, however problem has reared it's ugly head again, and excluding all firebase components doesn't fix it, so my original fix was merely a plaster.
I am now using Firebase Test Lab to conduct runs (it's much quicker than pushing builds to Google Play, and having to bump the number each time and wait a day). Firebase Test Lab fails the same way, and much quicker (you get 5 runs a day for free). I can now see that it's something related to R8 code obfuscation.
Setting minifyEnabled to false and it works just fine, setting it to true, it crashes every time.
So it appears to be be releated to this R8 bug: https://issuetracker.google.com/issues/213617215#comment22
For me, I have worked around this issue by adding this to my Proguard rules file. It now passes on Firebase Test Lab.
-keep class androidx.core.graphics.drawable.** { *; }

Is Android Gradle Plugin test apk builder automatically multidexing my test apk? If so, why is this generating "NoSuchMethodError"

This all started with this error when running instrumentation tests:
java.lang.NoSuchMethodError: No static method closeQuietly(Ljava/net/ServerSocket;)V in class Lokhttp3/internal/Util; or its super classes (declaration of 'okhttp3.internal.Util' appears in /data/app/com.example-vKdPJoTLl49ntRbZfsRBqQ==/base.apk!classes2.dex)
at okhttp3.mockwebserver.MockWebServer$2.execute(MockWebServer.java:333)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
I went looking into the generated test.apk to see what was present/missing in dex files.
First thing I noticed is that the test apk has two .dex files. Why? I'm not using multidex (and IIRC multidex doesn't work on test apks anyways). I then added up the two "referenced method counts" and sure enough I'm over the 65k limit. So is AGP auto-multi-dexing my test apk?
Furthermore I see the "missing" method in the first dex.
Why is it listed as a "reference" not a "defined" method? It's not like there is an OkHttp Util class provided by the framework.
In the initial crash, it said it can't find method in classes2.dex. Why is it looking in classes2.dex? Why not look in both?
I'm keeping everything in my test apk (using proguard but keeping everything) (maybe this explains the +65k ref methods). So why is this getting stripped/screwed with in the first place.
UPDATE:
It turns out this method was needed in my app .apk (not the test .apk). When I updated my proguard rules for the app apk everything worked. I still don't know why this class is needed in-app?
java.lang.NoSuchMethodError:
No static method closeQuietly(java.net.ServerSocket) in class okhttp3.internal.Util;
method closeQuietly(java.net.ServerSocket) might not be in use.
to explicitly keep it:
-keep class okhttp3.internal.Util {
public static void closeQuietly(java.net.ServerSocket);
}
I don't know how I didn't know this but apparently multidex is enabled by default on API 21+
https://developer.android.com/studio/build/multidex#mdex-on-l
Therefore, if your minSdkVersion is 21 or higher multidex is enabled
by default, and you do not need the multidex support library.
So yeah, this is normal. What might be happening is that the class is just not in the first dex file.

Cryptic FCM 10.2.0 Failure - FirebaseInitProvider, NoSuchMethodError

I'm trying to add Firebase Cloud Messaging to my app with no success, as I get the following exception:
1 27336-27336/com.company.here E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.company.here, PID: 27336
java.lang.NoSuchMethodError: No static method zzb(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; in class Lcom/google/android/gms/common/internal/zzaa; or its super classes (declaration of 'com.google.android.gms.common.internal.zzaa' appears in /data/app/com.company.here.debug-1/base.apk)
at com.google.firebase.provider.FirebaseInitProvider.zza(Unknown Source)
at com.google.firebase.provider.FirebaseInitProvider.attachInfo(Unknown Source)
at android.app.ActivityThread.installProvider(ActivityThread.java:5883)
at android.app.ActivityThread.installContentProviders(ActivityThread.java:5475)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5414)
at android.app.ActivityThread.-wrap2(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1546)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6154)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
Here's what I've checked so far:
google-services is set to 3.0.0 in the project gradle file
I only have one dependency related to firebase, which is 10.2.0
I have jack enabled so I can use lambdas
I have ReactiveAndroid and Retrofit incorporated into the project, perhaps they're interacting badly? That said I didn't see anything suspicious when running ./gradlew app:dependencies
Tried multiDexEnabled = true, didn't make a difference
Had a custom applicationIdSuffix for debug builds, attempted to remove it but it also didn't make a difference
Positioning of apply plug: 'com.google.gms.google-services doesn't seem to matter, currently at end of gradle file
What could be causing this? As best as I can tell, FirebaseInitProvider is calling an internal static method from attachInfo, which in turn attempts to call out to a static method on an SDK-private class that doesn't seem to exist.
So this was weird:
Nothing about my setup changed from last night except I ran the clean project command, added the firebase-core dependency and then built again, and everything started to work.
I had assumed that firebase-messaging pulled all of its dependencies in by itself, but that goes against the setup guide, and the crash makes sense if the internal class FirebaseInitProvider is attempting to access is in the firebase-core dependency.
You probably have a mismatch of one of your play-services dependencies and Firebase in your app level build.gradle.
Having google services at 3.0.0 in your project level build.gradle is fine (com.google.gms:google-services:3.0.0).
For example, if you have dependency com.google.android.gms:play-services-maps:10.2.0 and com.google.firebase:firebase-invites:10.2.0 they must both be the same version 10.2.0.

NoClassDefFoundError ObjenesisStd on Android API 19

I'm getting the following crashing during an instrumentation test ONLY on emulators running API v19. If I run on newer versions everything works fine.
03-01 20:26:18.781 2878-2878/? E/MonitoringInstrumentation: Exception
encountered by: Thread[main,5,main]. Dumping thread state to outputs
and pining for the fjords.
java.lang.NoClassDefFoundError: org.objenesis.ObjenesisStd
at org.mockito.internal.creation.jmock.ClassImposterizer.(ClassImposterizer.java:36)
at org.mockito.internal.creation.jmock.ClassImposterizer.(ClassImposterizer.java:29)
at org.mockito.internal.util.MockCreationValidator.isTypeMockable(MockCreationValidator.java:17)
at org.mockito.internal.util.MockCreationValidator.validateType(MockCreationValidator.java:21)
at org.mockito.internal.creation.MockSettingsImpl.validatedSettings(MockSettingsImpl.java:133)
at org.mockito.internal.creation.MockSettingsImpl.confirm(MockSettingsImpl.java:127)
at org.mockito.internal.MockitoCore.mock(MockitoCore.java:50)
at org.mockito.Mockito.mock(Mockito.java:1243)
at org.mockito.Mockito.mock(Mockito.java:1120)
The problem is this line:
java.lang.NoClassDefFoundError: org.objenesis.ObjenesisStd
My app is multi-dex, and I'm using dexmaker-mockito for androidTestCompile. I'm at a complete loss why this only breaks on an older API. It started happening when I added another module to my project, which is a pure java module with no dependency on mockito.
This exception (ClassNotFoundException) tells you about a unmet dependency at runtime: the JVM needs to load a class; which is not present in the class path.
Here it is Mockito that needs Objenesis. Normally that library should be pulled automatically when you a system like maven and give the correct dependency to Mockito.

Mocking Google Analytics v4

I'm trying to test my code that depends on Google Analytics SDK v4.
Specifically I'm trying to mock com.google.android.gms.analytics.Tracker with Mockito.
Tracker tracker = Mockito.mock(Tracker.class); yields this error.
Are there any approaches to take? The only thing I can think of is to create my own wrapper.
I believe the code snipped at fault is Tracker.class - as this will instantiate the class, which in turn throws the VerifyError. This is not an issue with your code, but is a limitation of Google Play Services. The issue has been reported in the Robolectric project and here.
I used the solution provided in the second link by SuperJugy, by inserting the following to the bottom of my Gradle build file:
tasks.withType(Test) {
test {
// set JVM arguments for the test JVM(s)
jvmArgs '-XX:-UseSplitVerifier'
}
}
To get it working in Android Studio I had to add the VM Option -noverify to my test build configuration.
I think you may be able to work-around the problem using a wrapper, so long as the wrapper code never instantiates the Tracker class. However this may not be easy (or possible?), please let me know if end up going down this path and succeed!

Categories

Resources