I want to mock DynamicRealm with Mockito without magic in the project.
Underlying exception : java.lang.IllegalArgumentException: Could not create type
at org.glucosio.android.db.MigrationTest.<init>(MigrationTest.java:23)
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.runners.BlockJUnit4ClassRunner.createTest(BlockJUnit4ClassRunner.java:217)
at org.junit.runners.BlockJUnit4ClassRunner$1.runReflectiveCall(BlockJUnit4ClassRunner.java:266)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.BlockJUnit4ClassRunner.methodBlock(BlockJUnit4ClassRunner.java:263)
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.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
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.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)
Caused by: java.lang.IllegalArgumentException: Could not create type
at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:140)
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.SubclassByteBuddyMockMaker.createMockType(SubclassByteBuddyMockMaker.java:71)
at org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker.createMock(SubclassByteBuddyMockMaker.java:42)
at org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker.createMock(ByteBuddyMockMaker.java:25)
at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:35)
at org.mockito.internal.MockitoCore.mock(MockitoCore.java:65)
at org.mockito.Mockito.mock(Mockito.java:1821)
at org.mockito.Mockito.mock(Mockito.java:1734)
... 35 more
Caused by: java.lang.NoClassDefFoundError: io/reactivex/Flowable
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
at java.lang.Class.getDeclaredMethods(Class.java:1975)
at net.bytebuddy.description.method.MethodList$ForLoadedMethods.<init>(MethodList.java:90)
at net.bytebuddy.description.type.TypeDescription$ForLoadedType.getDeclaredMethods(TypeDescription.java:7271)
at net.bytebuddy.description.type.TypeDescription$Generic$OfNonGenericType.getDeclaredMethods(TypeDescription.java:3358)
at net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection.getDeclaredMethods(TypeDescription.java:5253)
at net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy$Default$5.doExtractConstructors(ConstructorStrategy.java:157)
at net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy$Default.extractConstructors(ConstructorStrategy.java:176)
at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.applyConstructorStrategy(SubclassDynamicTypeBuilder.java:185)
at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:162)
at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:155)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:2639)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator.make(DynamicType.java:2741)
at org.mockito.internal.creation.bytebuddy.SubclassBytecodeGenerator.mockClass(SubclassBytecodeGenerator.java:120)
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)
... 46 more
Caused by: java.lang.ClassNotFoundException: io.reactivex.Flowable
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 64 more
org.mockito.exceptions.base.MockitoException:
Mockito cannot mock this class: class io.realm.DynamicRealm.
DynamicRealmRealm version(s): 4.3.3
Realm sync feature enabled: no
Android Studio version: 3.0.1
Which Android version and device: none
UPDATE
To make tests not fail I have to add next to my build.gradle:
testImplementation 'io.reactivex.rxjava2:rxjava:xxx'
Have you tried https://stackoverflow.com/a/47062181/2413303?
What a difference from my workaround with SO workaround? Updated text of issue to make clear that issue is not harming with a workaround.
The SO workaround adds the minimum required classes that look like RxJava, but doesn't actually add RxJava, only 3 classes.
My workaround also doesn't add Rx to the production code. God knows what next Rx dependency I have to fake with Realm update.
It is also easier to explain to newcomers in one place.
God knows what next Rx dependency I have to fake with Realm update.
My hopes are that next time, Kotlin usage will be so prevalent that Rx support can be ripped out into its own separate module, and would be a method like RxRealm.asFlowable(results), with additional Kotlin extensions that would add it as an extension method to RealmResults.
I wonder why it was not done as a separate dependency from the start. Let's wait for the Realm people reply.
@emartynov actually, that's a simple answer - because Fluency! :+1:
Buuut I actually don't really like current the Rx integration. You need an open Realm and thread-local RealmResults just to pass a RealmConfiguration.
I'd much prefer something like
Flowable<RealmResults<Blah>> flowable = RxRealm.createResults(configuration, (realm) -> realm.where(Blah.class).findAll());
Flowable<RealmResults<Blah>> flowable = RxRealm.createResults((realm) -> realm.where(Blah.class).findAll()); // default config
but that's just my opinion :D
The current support for RxJava is the result of a lot of tradeoffs, but we are continuously evaluating these tradeoffs, which means that it is possible the API might change in the future.
But the primary reason for the current API is, as @Zhuinden points out, the fluid nature of it. It makes it a lot more discoverable as you can find the method directly on the RealmResults and it is easier to autocomplete.
Obviously, this kind of functionality is a prime example of where extension functions would be great, so I strongly suspect that when/if Kotlin support reaches a critical threshold, we will switch to extension functions and a RxRealm class, but currently the split is still ~80/20 for Java.
The problem with Class Loaders is unfortunate. We could just have included RxJava as a required dependency since we expose the types directly in our API, but since method count is also important to developers, we opted for having it as an optional dependency.
The normal Android ClassLoader can handle this just fine (which was the reason it was acceptable), but unfortunately, some libraries do class loading by themselves. Jackson and Mockito being the prime examples and these do not handle this well, which means that workarounds are required when using these.
So to summarize, we think that the current API is the best compromise between a lot of conflicting interests, and you just happened to stumble upon one of the downsides of the approach we have taken. But if we have chosen otherwise, a lot more people would have been affected.
Most helpful comment
The current support for RxJava is the result of a lot of tradeoffs, but we are continuously evaluating these tradeoffs, which means that it is possible the API might change in the future.
But the primary reason for the current API is, as @Zhuinden points out, the fluid nature of it. It makes it a lot more discoverable as you can find the method directly on the
RealmResultsand it is easier to autocomplete.Obviously, this kind of functionality is a prime example of where extension functions would be great, so I strongly suspect that when/if Kotlin support reaches a critical threshold, we will switch to extension functions and a
RxRealmclass, but currently the split is still ~80/20 for Java.The problem with Class Loaders is unfortunate. We could just have included RxJava as a required dependency since we expose the types directly in our API, but since method count is also important to developers, we opted for having it as an optional dependency.
The normal Android ClassLoader can handle this just fine (which was the reason it was acceptable), but unfortunately, some libraries do class loading by themselves. Jackson and Mockito being the prime examples and these do not handle this well, which means that workarounds are required when using these.
So to summarize, we think that the current API is the best compromise between a lot of conflicting interests, and you just happened to stumble upon one of the downsides of the approach we have taken. But if we have chosen otherwise, a lot more people would have been affected.