Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't mock FirebaseRemoteConfig using mockito-inline #1078

Closed
solcott opened this issue May 11, 2017 · 8 comments
Closed

Can't mock FirebaseRemoteConfig using mockito-inline #1078

solcott opened this issue May 11, 2017 · 8 comments

Comments

@solcott
Copy link

solcott commented May 11, 2017

This occurs when using mockito 2.7.22, openjdk version "1.8.0_112-release", and Mac OS 10.12.4

When using mockito-inline in unit test on an Android project it is unable to mock FirebaseRemoteConfig. When I try to call when(mockFirebaseRemoteConfig.getBoolean("test_key")).thenReturn(true) I get a null pointer exception in the actual FirebaseRemoteConfig code:

java.lang.NullPointerException at com.google.firebase.remoteconfig.FirebaseRemoteConfig.getBoolean(Unknown Source) at com.google.firebase.remoteconfig.FirebaseRemoteConfig.getBoolean(Unknown Source) at com.example.solcott.mockfirebaseremoteconfig.ExampleUnitTest.addition_isCorrect(ExampleUnitTest.java:26) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:68) at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:74) at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39) at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:161) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.intellij.rt.execution.application.AppMainV2.main(AppMainV2.java:131)

When I switch to using mockito-core it works fine. Also, when using mockito-inline and mockito-android together it also works.

Sample code that reproduces this is at https://github.com/solcott/MockFirebaseRemoteConfig

Run from the command line using ./gradlew testDebugUnitTest or from inside Android Studio.

@mockitoguy
Copy link
Member

Thank you for reporting and for offering us reproducible sample! What version of Mockito do you use?

When we have a chance we will look into this problem. In the meantime, if you (or somebody else) has the courage to dive into Mockito codebase and push a bit harder to identify the problem, that would be AWESOME. Reproducible sample will be very helpful - thanks again for that!

@solcott
Copy link
Author

solcott commented May 25, 2017

This happened on 2.8.9 and 2.7.22

@frapontillo
Copy link

frapontillo commented May 27, 2017

I believe I have a similar issue mocking FirebaseAuth.
The funny thing is that disabling mock-maker-inline makes everything work again.

org.mockito.exceptions.base.MockitoException: 
Mockito cannot mock this class: class com.google.firebase.auth.FirebaseAuth.

If you're not sure why you're getting this error, please report to the mailing list.


Java               : 1.8
JVM vendor name    : Oracle Corporation
JVM vendor version : 25.131-b11
JVM name           : Java HotSpot(TM) 64-Bit Server VM
JVM version        : 1.8.0_131-b11
JVM info           : mixed mode
OS name            : Mac OS X
OS version         : 10.12.3


You are seeing this disclaimer because Mockito is configured to create inlined mocks.
You can learn about inline mocks and their limitations under item #39 of the Mockito class javadoc.

Underlying exception : org.mockito.exceptions.base.MockitoException: Could not modify all classes [interface com.google.android.gms.internal.zzbti, class com.google.firebase.auth.FirebaseAuth]

	at com.myapp.Test.beforeEach(Test.kt:216)
	at com.myapp.Test.<init>(Test.kt:42)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at java.lang.Class.newInstance(Class.java:442)
	at io.kotlintest.KTestJUnitRunner.<init>(KTestJUnitRunner.kt:9)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:104)
	at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:86)
	at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
	at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
	at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
	at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:33)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:49)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.rt.execution.application.AppMainV2.main(AppMainV2.java:131)
Caused by: org.mockito.exceptions.base.MockitoException: Could not modify all classes [interface com.google.android.gms.internal.zzbti, class com.google.firebase.auth.FirebaseAuth]
	at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:138)
	at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:346)
	at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:161)
	at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:355)
	... 27 more
Caused by: java.lang.IllegalStateException: 
Byte Buddy could not instrument all classes within the mock's type hierarchy

This problem should never occur for javac-compiled classes. This problem has been observed for classes that are:
 - Compiled by older versions of scalac
 - Classes that are part of the Android distribution
	at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.triggerRetransformation(InlineBytecodeGenerator.java:120)
	at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.mockClass(InlineBytecodeGenerator.java:97)
	at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator$1.call(TypeCachingBytecodeGenerator.java:37)
	at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator$1.call(TypeCachingBytecodeGenerator.java:34)
	at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:138)
	at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:346)
	at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:161)
	at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:355)
	at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator.mockClass(TypeCachingBytecodeGenerator.java:32)
	at org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker.createMockType(InlineByteBuddyMockMaker.java:201)
	at org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker.createMock(InlineByteBuddyMockMaker.java:182)
	at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:35)
	at org.mockito.internal.MockitoCore.mock(MockitoCore.java:63)
	at org.mockito.Mockito.mock(Mockito.java:1729)
	... 27 more
Caused by: java.lang.IllegalStateException: Inconsistent frame length for public void com.google.firebase.auth.FirebaseAuth.zza(com.google.firebase.auth.FirebaseUser,com.google.android.gms.internal.zzbmn,boolean): 0
	at net.bytebuddy.asm.Advice$StackMapFrameHandler$Default.translateFrame(Advice.java:1187)
	at net.bytebuddy.asm.Advice$StackMapFrameHandler$Default.translateFrame(Advice.java:1141)
	at net.bytebuddy.asm.Advice$AdviceVisitor.visitFrame(Advice.java:6636)
	at net.bytebuddy.jar.asm.ClassReader.a(Unknown Source)
	at net.bytebuddy.jar.asm.ClassReader.b(Unknown Source)
	at net.bytebuddy.jar.asm.ClassReader.accept(Unknown Source)
	at net.bytebuddy.jar.asm.ClassReader.accept(Unknown Source)
	at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining.create(TypeWriter.java:2910)
	at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1628)
	at net.bytebuddy.dynamic.scaffold.inline.RedefinitionDynamicTypeBuilder.make(RedefinitionDynamicTypeBuilder.java:171)
	at net.bytebuddy.dynamic.scaffold.inline.AbstractInliningDynamicTypeBuilder.make(AbstractInliningDynamicTypeBuilder.java:92)
	at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:2560)
	at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.transform(InlineBytecodeGenerator.java:167)
	at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
	at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428)
	at sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
	at sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:144)
	at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.triggerRetransformation(InlineBytecodeGenerator.java:117)
	... 40 more

@emarc-m
Copy link

emarc-m commented May 27, 2017

I've got this to work by disabling bytecode verification, as suggested here. This might not be the best solution but the issue is still being investigated by the team.

BTW, to make it work for Android Studio, you need to manually add -noverify to the run configuration's VM options. Android Studio does not pick up the gradle option.

@frapontillo
Copy link

@emarc-m I can confirm your fix works, even if I leave inline mocks enabled (which I need since I'm testing Kotlin classes, which are final by default).

Put this in your build.gradle:

android {
    testOptions {
        unitTests.all {
            jvmArgs '-noverify'
        }
    }
}

And, if you run tests outside of Gradle (from Android Studio or, in my case, IntelliJ IDEA) set -noverify in the VM options of the test configuration.

@raphw
Copy link
Member

raphw commented May 27, 2017

The answer lies in the stack trace already: Caused by: java.lang.IllegalStateException: Inconsistent frame length for public void com.google.firebase.auth.FirebaseAuth.zza.

This happens for example when instrumenting obfuscated classes that do not correctly retain type information within a method what is triggered by tools like ProGuard. This makes it impossible to add code at the end of a method what is required to implement the mocking logic. Ironically, this obfuscation does not make it harder to decompile code as the method signature needs to be retained anyways. I consider it a bug in ProGuard.

@TimvdLippe
Copy link
Contributor

Closing this issue per above comment. Sadly there is nothing actionable here as we are hit by the bug in ProGuard. The workaround is to use -noverify as posted 2 comments above.

@raphw
Copy link
Member

raphw commented May 29, 2017

I filed a bug report at Proguard. I did some tests and this also breaks APM tools, for example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants