Closed pouyabs5 closed 7 years ago
The only way to work around it for now is set RealmCore#libraryIsLoaded
to true
before calling Realm#init(Context)
by using reflection.
zaki50, would you discribe, how can I do this by reflecting? I have such problem. I just had to add RealmCore.loadLibrary(this) in my application class before calling Realm.init(this), yes?
See also this example: https://github.com/realm/realm-java/tree/master/examples/unitTestExample
@zaki50, i set RealmCore#libraryIsLoaded
before calling Realm#init(context)
but i still face same error and when i debug the RealmConfig.loadLibrary()
functions, the issue is for this :
java.lang.UnsatisfiedLinkError: no realm-jni in java.library.path
Please take a look at https://github.com/robolectric/robolectric/issues/1389
I created this project :
https://github.com/pouyabs5/realm-test.git
after running unit test i face to this:
Failed to transform class with name io.realm.Realm. Reason: rx.Observable
Ah, you should create a package named rx
, and you should create an empty class inside it called Observable
package rx;
public class Observable {
}
I'm getting this Exception when my test is running:
java.lang.UnsatisfiedLinkError: Can't load library: C:\Users\Nikita\AppData\Local\Temp\android-tmp-robolectric8414149647152901922\app_lib\realm-jni.dll
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1827)
at java.lang.Runtime.load0(Runtime.java:809)
at java.lang.System.load(System.java:1086)
at com.getkeepsafe.relinker.SystemLibraryLoader.loadPath(SystemLibraryLoader.java:29)
at com.getkeepsafe.relinker.ReLinkerInstance.loadLibraryInternal(ReLinkerInstance.java:198)
at com.getkeepsafe.relinker.ReLinkerInstance.loadLibrary(ReLinkerInstance.java:136)
at com.getkeepsafe.relinker.ReLinker.loadLibrary(ReLinker.java:70)
at com.getkeepsafe.relinker.ReLinker.loadLibrary(ReLinker.java:51)
at io.realm.internal.RealmCore.loadLibrary(RealmCore.java:59)
at io.realm.Realm.init(Realm.java:187)
at com.tst.nikita.newsapp.presentation.NewsApplication.onCreate(NewsApplication.java:41)
at org.robolectric.internal.ParallelUniverse.setUpApplicationState(ParallelUniverse.java:141)
at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:234)
at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:171)
at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:47)
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.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:137)
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:119)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
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.AppMain.main(AppMain.java:144)
Process finished with exit code -1
I did my method setUp() like here https://github.com/realm/realm-java/blob/master/examples/unitTestExample/src/test/java/io/realm/examples/unittesting/ExampleActivityTest.java but Realm.init(Context context) in my Application.class is running and I'm gettin this Exception.
My test class:
@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21)
@PowerMockIgnore({ "org.robolectric.*", "org.mockito.*", "android.*"})
@SuppressStaticInitializationFor("io.realm.internal.Util")
@PrepareForTest({Realm.class, RealmConfiguration.class, RealmQuery.class, RealmResults.class, RealmCore.class, RealmLog.class})
public class RealmCacheTest {
Realm mockRealm;
@Rule
public PowerMockRule rule = new PowerMockRule();
@Before
public void setUp() throws Exception {
mockStatic(RealmCore.class);
mockStatic(RealmLog.class);
mockStatic(Realm.class);
mockStatic(RealmConfiguration.class);
RealmCore.loadLibrary((RuntimeEnvironment.application));
Realm.init(RuntimeEnvironment.application);
final Realm mockRealm = mock(Realm.class);
final RealmConfiguration mockRealmConfig = mock(RealmConfiguration.class);
doNothing().when(RealmCore.class);
whenNew(RealmConfiguration.class).withAnyArguments().thenReturn(mockRealmConfig);
when(Realm.getDefaultInstance()).thenReturn(mockRealm);
this.mockRealm = mockRealm;
}
@Test
public void shouldBeAbleToGetDefaultInstance() {
assertThat(Realm.getDefaultInstance(), is(mockRealm));
}
}
Since you mocked everything, no need to call RealmCore.loadLibrary((RuntimeEnvironment.application));
and Realm.init(RuntimeEnvironment.application);
. Will it work if you remove those two lines?
I ran into the same / similar issue. After upgrading to Realm 2.x, a lot of robolectic tests failed. The root cause was the Realm.init
addition to the Application
.
Since I didn't want to touch any of the tests, I ended up using reflection to make Realm.init
think it is already initialized. Setting libraryIsLoaded
to true was not enough - it was still causing exceptions.
Thoughts on this approach?
For me the above mentioned mock approach with mockStatic/PrepareForTest only worked with PowerMockRule
and not with MockitoRule
.
Here is the code snippet to fake init Realm.
package io.realm;
import android.content.Context;
import java.lang.reflect.Field;
public class FakeRealmInitForTestEnvironment {
public static void init(Context context) {
try {
Field applicationContext = BaseRealm.class.getDeclaredField("applicationContext");
applicationContext.set(applicationContext, context);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@pouyabs5 @nikiJava Did suggestion by @thorbenprimke help you?
@kneth unfortunately no!
@pouyabs5 Not entirely sure what you are doing differently, but I running the unit tests from https://github.com/realm/realm-java/blob/master/examples/unitTestExample/src/test/java/io/realm/examples/unittesting/ExampleRealmTest.java seems to work fine. So I would probably start by comparing those with what you are doing to see if you can spot the difference.
Note that we recently merged a small change to master
that removes the final
modifier from all public API classes. This should make mocking with Mockito a lot easier.
I am using the example that @cmelchior just pointed out and originally I had Realm.init(getApplicationContext())
in my Application class, but after moving that line to the first activity of my app it solved this issue.
@pouyabs5 Can you try to move Realm.init()
as @Reline suggests?
I assume that removing Realm.init()
solved your issue.
No, it still not working...
I tried everything for testing Realm with unit testing and now I'm even thinking about remove Realm from project at all because there is no way that I could test my functionalities or services with unit tests and there already was few more issues. I don't understand how/why other people and you guys from Realm didn't take care about this.
I 've lost like 3 weeks searching for solution, and I've lost my patience about this framework and testing. This is not professional solution for ORM for Android. In project I use also Dagger, and I've tried even to init Realm with some inMemory configuration, but Realm.init is not working with RuntimeEnvironment.application etc.
This is my test class with some stupid case:
@RunWith(RobolectricTestRunner.class) @Config(application = TestApp.class, constants = BuildConfig.class, sdk = 21) @PowerMockIgnore({ "org.robolectric.", "org.mockito.", "android.*"}) @SuppressStaticInitializationFor("io.realm.internal.Util") @PrepareForTest({Realm.class, RealmConfiguration.class, RealmQuery.class, RealmResults.class, RealmCore.class, RealmLog.class}) public class DocumentServiceTest {
Realm mockRealm;
@Rule
public PowerMockRule rule = new PowerMockRule();
@Before
public void setUp() throws Exception {
mockStatic(RealmCore.class);
mockStatic(RealmLog.class);
mockStatic(Realm.class);
mockStatic(RealmConfiguration.class);
RealmCore.loadLibrary((RuntimeEnvironment.application));
// Realm.init(RuntimeEnvironment.application);
final Realm mockRealm = mock(Realm.class);
final RealmConfiguration mockRealmConfig = mock(RealmConfiguration.class);
doNothing().when(RealmCore.class);
whenNew(RealmConfiguration.class).withAnyArguments().thenReturn(mockRealmConfig);
when(Realm.getDefaultInstance()).thenReturn(mockRealm);
this.mockRealm = mockRealm;
}
@Test
public void shouldBeAbleToGetDefaultInstance() {
assertThat(Realm.getDefaultInstance(), is(mockRealm));
}
@Test
public void showsCorrectBillValue() {
SharedService sharedService = Mockito.mock(SharedService.class);
DocumentService documentService = new DocumentService(mockRealm, sharedService);
Document d = new Document();
documentService.saveDocument(d);
DocumentItem documentItem1 = new DocumentItem();
documentItem1.setKolicina(10.0);
documentItem1.setNetoCijena(10.0);
DocumentItem documentItem2 = new DocumentItem();
documentItem2.setKolicina(10.0);
documentItem2.setNetoCijena(10.0);
DocumentItem documentItem3 = new DocumentItem();
documentItem3.setKolicina(10.0);
documentItem3.setNetoCijena(10.0);
d.getDocumentItems().add(documentItem1);
d.getDocumentItems().add(documentItem2);
d.getDocumentItems().add(documentItem3);
String result = documentService.getDocumentBillValue(d);
assertThat(result, is("300.00"));
}
}
This throws some errors:
and here is the gradle dependencies:
dependencies { compile fileTree(dir: 'libs', include: ['*.jar'])
// DAGGER
compile "com.google.dagger:dagger:2.9"
apt "com.google.dagger:dagger-compiler:2.9"
testApt 'com.google.dagger:dagger-compiler:2.9'
androidTestApt 'com.google.dagger:dagger-compiler:2.9'
provided 'javax.annotation:jsr250-api:1.0'
// TESTING
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.2'
testCompile 'junit:junit:4.12'
testCompile "org.robolectric:robolectric:3.3"
testCompile 'org.mockito:mockito-core:2.7.22'
testCompile 'org.robolectric:shadows-support-v4:3.3'
testCompile 'org.powermock:powermock-module-junit4:1.6.5'
testCompile 'org.powermock:powermock-module-junit4-rule:1.6.5'
testCompile 'org.powermock:powermock-api-mockito:1.6.5'
testCompile 'org.powermock:powermock-classloading-xstream:1.6.5'
//DESIGN
compile 'com.android.support:appcompat-v7:24.2.1'
compile 'com.android.support:design:24.2.1'
compile 'com.github.ganfra:material-spinner:1.1.1'
//GSON & RETROFIT
compile 'com.google.code.gson:gson:2.6.2'
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
//FIREBASE
compile 'com.google.firebase:firebase-core:10.0.1'
compile 'com.google.firebase:firebase-messaging:10.0.1'
compile 'com.google.firebase:firebase-crash:10.0.1'
}
I DON'T GET IT REALLY, is there any way that I could test my services that uses 1 argument(Realm db).???
If not, than this solution(REALM) for mobile database, for some serious application is BAD....
com.thoughtworks.xstream.converters.ConversionException: Cannot convert type
This is an error in Robolectric 's classloading in conjunction with Powermock, see https://github.com/robolectric/robolectric/issues/2208
(although it should have been fixed by 3.3....)
Ok, If I can't test this with Robolectric, what should I do then? I should test it with what? (Mockito, JUnit...)
Is there any complete solution for unit testing?
@erudonja1 Please keep a civil tone (https://realm.io/conduct/ ). We all know it is frustrating if things do not work, but yelling will not get you anywhere.
Did you see our unit testing example which uses Robolectric: https://github.com/realm/realm-java/blob/master/examples/unitTestExample/src/test/java/io/realm/examples/unittesting/ExampleRealmTest.java
Note that in recent releases all our public classes are no longer final which means they should be mockable my Mockito.
@cmelchior i think that works because it uses testCompile "org.robolectric:robolectric:3.0"
, and robolectric 3.1+ broke something with their classloading that it seems they still haven't seem to have properly fixed when used in conjunction with Powermock.
Personally I could never get Robolectric to work in any slightly more complex setting, with or without Realm. -_-
Anyways, if Realm's static accessor methods Realm.getDefaultInstance()
are wrapped with something interface-y then PowerMock isn't needed, and testing should work. This issue stems from Robolectric not interacting properly with PowerMock, after all.
@cmelchior Don't worry, I'm not yelling at anyone, read my comment again, there is no "!!!" anywhere. The accent is in that I don't really understand how I could test my functionalities at all now. This should be your problem No.1 if you plan to use/recommend this ORM somewhere for professional usage. It's not like I have one solution for testing and I didn't make it. The frustration begins when you try 10000 different solutions from your comments and even gists that are incomplete, and neither one is working.
And what should I do now?
@Zhuinden are you saying that this solution worked before for some versions?
If it is, could you tell me which and for what version should I use then. please.
@erudonja1 as I said, Robolectric 3.1 broke PowerMock integration. But Robolectric 3.0 is rather old... the xstream
thing is what breaks it.
@Zhuinden I'm not so optimistic with that version neither but I'll try tomorrow again. I hope you were right about this... Thanks
@Zhuinden Nope... It still not working with 3.0 version.
Error message: com.thoughtworks.xstream.converters.ConversionException: ---- Debugging information ---- cause-exception : java.lang.IllegalStateException cause-message : Failed to transform class with name io.realm.Realm. Reason: rx.Observable class : org.junit.internal.runners.statements.InvokeMethod required-type : org.junit.internal.runners.statements.InvokeMethod converter-type : com.thoughtworks.xstream.converters.reflection.ReflectionConverter path : /org.powermock.modules.junit4.rule.PowerMockStatement$1/outer-class/fNext/next/target line number : 12 class[1] : org.junit.internal.runners.statements.RunBefores class[2] : org.powermock.modules.junit4.rule.PowerMockStatement class[3] : org.powermock.modules.junit4.rule.PowerMockStatement$1 version : not available
gradle dependencies: testCompile 'junit:junit:4.12' testCompile "org.robolectric:robolectric:3.0" testCompile 'org.mockito:mockito-core:2.7.22' testCompile 'org.robolectric:shadows-support-v4:3.0' testCompile 'org.powermock:powermock-module-junit4:1.6.5' testCompile 'org.powermock:powermock-module-junit4-rule:1.6.5' testCompile 'org.powermock:powermock-api-mockito:1.6.5' testCompile 'org.powermock:powermock-classloading-xstream:1.6.5'
@erudonja1 try to create a dummy class rx.Observable
. See https://realm.io/docs/java/latest/#jackson-databind
@beeender but where to put that package and class? Should I create it into my testProject, androidTestProject or just into my project?
If I just create new package with class as it is mentioned it throws this error message:
java.lang.NoClassDefFoundError: org/mockito/cglib/proxy/Enhancer
at org.powermock.api.extension.proxyframework.ProxyFrameworkImpl.isProxy(ProxyFrameworkImpl.java:52)
at org.powermock.reflect.internal.WhiteboxImpl.getUnmockedType(WhiteboxImpl.java:1689)
at org.powermock.reflect.internal.WhiteboxImpl.getType(WhiteboxImpl.java:2111)
at org.powermock.reflect.internal.WhiteboxImpl.invokeConstructor(WhiteboxImpl.java:1329)
at org.powermock.reflect.Whitebox.invokeConstructor(Whitebox.java:511)
at org.powermock.tests.utils.impl.MockPolicyInitializerImpl.invokeInitializeInterceptionSettingsFromClassLoader(MockPolicyInitializerImpl.java:151)
at org.powermock.tests.utils.impl.MockPolicyInitializerImpl.refreshPolicies(MockPolicyInitializerImpl.java:138)
at org.powermock.modules.junit4.rule.PowerMockStatement$1.run(PowerMockRule.java:82)
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.powermock.reflect.internal.WhiteboxImpl.performMethodInvocation(WhiteboxImpl.java:1899)
at org.powermock.reflect.internal.WhiteboxImpl.doInvokeMethod(WhiteboxImpl.java:801)
at org.powermock.reflect.internal.WhiteboxImpl.invokeMethod(WhiteboxImpl.java:666)
at org.powermock.reflect.Whitebox.invokeMethod(Whitebox.java:401)
at org.powermock.classloading.AbstractClassloaderExecutor.getResult(AbstractClassloaderExecutor.java:69)
at org.powermock.classloading.AbstractClassloaderExecutor.executeWithClassLoader(AbstractClassloaderExecutor.java:59)
at org.powermock.classloading.SingleClassloaderExecutor.execute(SingleClassloaderExecutor.java:67)
at org.powermock.classloading.AbstractClassloaderExecutor.execute(AbstractClassloaderExecutor.java:43)
at org.powermock.modules.junit4.rule.PowerMockStatement.evaluate(PowerMockRule.java:75)
at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:251)
at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:188)
at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:54)
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.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:152)
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:119)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
@erudonja1 The exception message shows another problem which I have no idea about ... sounds like something wrong between powermock and robolectric.
@erudonja1 once you fixed issue by creating rx.Observable
class, now this problem is a version mismatch between Mockito 2.x and Powermock 1.6.5! see https://groups.google.com/forum/#!topic/powermock/cE4T40Xa_wc
Powermock only has experimental support of Mockito 2.x (in 1.7.0 RC versions), see https://github.com/powermock/powermock/wiki/Mockito-2-(Maven)
The Powermock 2.0 release is scheduled for June 2017.
Another option is to revert to Mockito 1.x.
@Zhuinden @cmelchior @beeender Thank you guys!
SOLUTION Finally this is solution that works partially:
gradle: dependencies { compile fileTree(dir: 'libs', include: ['*.jar'])
// DAGGER
compile "com.google.dagger:dagger:2.9"
apt "com.google.dagger:dagger-compiler:2.9"
testApt 'com.google.dagger:dagger-compiler:2.9'
androidTestApt 'com.google.dagger:dagger-compiler:2.9'
provided 'javax.annotation:jsr250-api:1.0'
// TESTING
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.2'
testCompile 'junit:junit:4.12'
testCompile "org.robolectric:robolectric:3.0"
testCompile 'org.mockito:mockito-core:1.10.19'
testCompile 'org.robolectric:shadows-support-v4:3.0'
testCompile 'org.powermock:powermock-module-junit4:1.6.5'
testCompile 'org.powermock:powermock-module-junit4-rule:1.6.5'
testCompile 'org.powermock:powermock-api-mockito:1.6.5'
testCompile 'org.powermock:powermock-classloading-xstream:1.6.5'
//DESIGN
compile 'com.android.support:appcompat-v7:24.2.1'
compile 'com.android.support:design:24.2.1'
compile 'com.github.ganfra:material-spinner:1.1.1'
//GSON & RETROFIT
compile 'com.google.code.gson:gson:2.6.2'
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
//FIREBASE
compile 'com.google.firebase:firebase-core:10.0.1'
compile 'com.google.firebase:firebase-messaging:10.0.1'
compile 'com.google.firebase:firebase-crash:10.0.1'
}
I also needed to create this class and package, somewhere in project root:
package rx; public class Observable { }
Then you could setUp before test like this:
@RunWith(RobolectricGradleTestRunner.class) @Config(application = TestApp.class, constants = BuildConfig.class, sdk = 21) @PowerMockIgnore({ "org.robolectric.", "org.mockito.", "android.*"}) @SuppressStaticInitializationFor("io.realm.internal.Util") @PrepareForTest({Realm.class, RealmConfiguration.class, RealmQuery.class, RealmResults.class, RealmCore.class, RealmLog.class}) public class DocumentServiceTest {
Realm mockRealm;
@Rule
public PowerMockRule rule = new PowerMockRule();
@Before
public void setUp() throws Exception {
mockStatic(RealmCore.class);
mockStatic(RealmLog.class);
mockStatic(Realm.class);
mockStatic(RealmConfiguration.class);
RealmCore.loadLibrary((RuntimeEnvironment.application));
Realm.init(RuntimeEnvironment.application);
final Realm mockRealm = mock(Realm.class);
final RealmConfiguration mockRealmConfig = mock(RealmConfiguration.class);
doNothing().when(RealmCore.class);
whenNew(RealmConfiguration.class).withAnyArguments().thenReturn(mockRealmConfig);
when(Realm.getDefaultInstance()).thenReturn(mockRealm);
this.mockRealm = mockRealm;
}
}
Also I had to override my application class, onCreate method, because in there I was initializing Dagger...
public class TestApp extends Application {
@Override
public void onCreate() {
}
}
CONCLUSION: Realm.init(RuntimeEnvironment.application) in UnitTest wont' throw error, but will not work either with this piece of code. For example when you try to get some instance of InMemory database in unit test, compiler will say you that Realm.init is not triggered before that. BUT you can mock database(which is good)! So basically you can use it in constructors but if you try to save something to that database, you will get an error.
Soooooo, test those methods that are not using database directly...(it's better anything than nothing :()
@erudonja1 Robolectric claims that they've fixed the classloader issue that broke Powermock in your case in 3.3.1
Powermock 1.7.0 was released today (2017-06-16) with support for Mockito 2.x
See https://github.com/powermock/powermock/releases/tag/powermock-1.7.0
When i want to run this simple unit test :
I face this error :
and indicates to the error line in my class that extends from Application where i init the realm :