realm / realm-java

Realm is a mobile database: a replacement for SQLite & ORMs
http://realm.io
Apache License 2.0
11.47k stars 1.75k forks source link

ISSUE - Unit test Problem (librealm-jni.so) #3808

Closed pouyabs5 closed 7 years ago

pouyabs5 commented 8 years ago

When i want to run this simple unit test :

@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 19)
@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"})
@PrepareForTest({Realm.class, RealmLog.class})

public class ExampleUnitTest {

    @Rule
    public PowerMockRule rule = new PowerMockRule();
    Realm mockRealm;

    @Before
    public void setup() throws Exception {

        mockStatic(Realm.class);
        mockStatic(RealmLog.class);

        Realm mockRealm = PowerMockito.mock(Realm.class);

        when(Realm.getDefaultInstance()).thenReturn(mockRealm);
        when(Realm.getDefaultInstance()).thenReturn(mockRealm);
        this.mockRealm = mockRealm;
    }

    @Test
    public void shouldBeAbleToGetDefaultInstance() {
        assertThat(Realm.getDefaultInstance(), is(mockRealm));
    }
}

I face this error :

java.lang.UnsatisfiedLinkError: Can't load library: /tmp/android-tmp-robolectric2838690925023503406/app_lib/librealm-jni.so

and indicates to the error line in my class that extends from Application where i init the realm :

Realm.init(getApplicationContext());
        RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().deleteRealmIfMigrationNeeded().build();
        Realm.setDefaultConfiguration(realmConfiguration);
zaki50 commented 8 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.

nikiJava commented 8 years ago

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?

cmelchior commented 8 years ago

See also this example: https://github.com/realm/realm-java/tree/master/examples/unitTestExample

pouyabs5 commented 8 years ago

@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

kneth commented 7 years ago

Please take a look at https://github.com/robolectric/robolectric/issues/1389

pouyabs5 commented 7 years ago

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

Zhuinden commented 7 years ago

Ah, you should create a package named rx, and you should create an empty class inside it called Observable

package rx;

public class Observable {
}
nikiJava commented 7 years ago

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));
    }
}
beeender commented 7 years ago

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?

thorbenprimke commented 7 years ago

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();
        }
    }
}
kneth commented 7 years ago

@pouyabs5 @nikiJava Did suggestion by @thorbenprimke help you?

pouyabs5 commented 7 years ago

@kneth unfortunately no!

cmelchior commented 7 years ago

@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.

reline commented 7 years ago

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.

kneth commented 7 years ago

@pouyabs5 Can you try to move Realm.init() as @Reline suggests?

kneth commented 7 years ago

I assume that removing Realm.init() solved your issue.

erudonja1 commented 7 years ago

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:

com.thoughtworks.xstream.converters.ConversionException: Cannot convert type com.sun.org.apache.xerces.internal.dom.DeferredElementImpl to type org.w3c.dom.Node ---- Debugging information ---- message : Cannot convert type com.sun.org.apache.xerces.internal.dom.DeferredElementImpl to type org.w3c.dom.Node class : org.robolectric.manifest.AndroidManifest required-type : org.robolectric.manifest.AndroidManifest converter-type : com.thoughtworks.xstream.converters.reflection.ReflectionConverter path : /org.powermock.modules.junit4.rule.PowerMockStatement$1/outer-class/fNext/next/val$roboMethod/appManifest/applicationNode line number : 5101 class[1] : org.robolectric.RobolectricTestRunner$RobolectricFrameworkMethod class[2] : org.robolectric.RobolectricTestRunner$HelperTestRunner$1 class[3] : org.junit.internal.runners.statements.RunBefores class[4] : org.powermock.modules.junit4.rule.PowerMockStatement class[5] : org.powermock.modules.junit4.rule.PowerMockStatement$1 version : not available

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....

Zhuinden commented 7 years ago

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....)

erudonja1 commented 7 years ago

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?

cmelchior commented 7 years ago

@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.

Zhuinden commented 7 years ago

@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.

erudonja1 commented 7 years ago

@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?

erudonja1 commented 7 years ago

@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.

Zhuinden commented 7 years ago

@erudonja1 as I said, Robolectric 3.1 broke PowerMock integration. But Robolectric 3.0 is rather old... the xstream thing is what breaks it.

erudonja1 commented 7 years ago

@Zhuinden I'm not so optimistic with that version neither but I'll try tomorrow again. I hope you were right about this... Thanks

erudonja1 commented 7 years ago

@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'

beeender commented 7 years ago

@erudonja1 try to create a dummy class rx.Observable. See https://realm.io/docs/java/latest/#jackson-databind

erudonja1 commented 7 years ago

@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)
beeender commented 7 years ago

@erudonja1 The exception message shows another problem which I have no idea about ... sounds like something wrong between powermock and robolectric.

Zhuinden commented 7 years ago

@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.

erudonja1 commented 7 years ago

@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 :()

Zhuinden commented 7 years ago

@erudonja1 Robolectric claims that they've fixed the classloader issue that broke Powermock in your case in 3.3.1

see https://github.com/robolectric/robolectric/issues/2944

Zhuinden commented 7 years ago

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