robolectric icon indicating copy to clipboard operation
robolectric copied to clipboard

Support Realm

Open kneth opened this issue 10 years ago • 45 comments

A number of users of Realm (for Android) has asked if we support Roboelectric. Realm's development is very such driven by user feedback, and we would love to be able to let users do better testing using Roboelectric.

Realm relies on a core written in C++. We do have many JNI calls, and we need a .o file in order to get it to work. What does it require of Realm so Roboelectic can integrate support for it?

kneth avatar Nov 19 '14 12:11 kneth

If there are workarounds for testing an app with Robolectric that has Realm deeply wired into it, I'd like to hear about it. There was an approach once mocking the JNI calls but that only seems to work for Robolectric 1.x.

@kneth: How do you guys unit-test your apps?

freezy avatar Jan 19 '15 21:01 freezy

This would be awesome.

eygraber avatar Jan 20 '15 05:01 eygraber

@freezy Realm is a framework, and we run our unit tests on both emulators real devices to ensure that it will work for app developers.

Realm for Android builds on Realm core which is a C++ library. The code size is much larger and it is shared by OS X, iOS and Android bindings. We have a special test suite for that part.

The only apps we develop are small sample app. We monkey test them.

kneth avatar Jan 20 '15 11:01 kneth

@kneth: I figured since you guys are earning your money by providing "enterprise services", you might be aware how people unit-test their apps with Realm.

freezy avatar Jan 20 '15 12:01 freezy

@kneth and I chatted about this and there are a few things that need to happen in order for Robolectric to support Realm: Realm needs to build a version of their native code that will run on a desktop machine. Once this is done, the Robolectric gradle plugin needs to be modified to set java.library.path to point to the extracted JNI. There may be other things that surface once we start testing, but those two items are needed for us to get started.

erd avatar Jan 21 '15 01:01 erd

Great to see progress on this.

@kneth: Any estimation about how much work this involves would be very appreciated (and of course, if you're going to invest time into this).

freezy avatar Jan 21 '15 08:01 freezy

Hi, Brian from Realm here. We could very easily make a version for any one platform e.g. Mac, but then I assume people would start asking about Windows and various linux versions as well :-) So it quickly becomes somewhat hard to manage and distribute all those binaries. We are definitely going to do so at some point anyway. There are however a number of other feature request that currently are higher on the list, so giving a timeframe is really hard. @erd Do you have any stats on the popularity of various platforms that robolectric is used on?

bmunkholm avatar Jan 21 '15 08:01 bmunkholm

Roboelectric is quite popular on android devices and support or errors with realm is big problem for our app

hanibalsk avatar Jan 28 '15 15:01 hanibalsk

@bmunkholm - I don't have any data on actual usage, but anecdotally, I'm pretty sure that Mac and Linux would be the majority.

erd avatar Jan 28 '15 21:01 erd

@bmunkholm Any possibility of making this a priority? Roboelectric is the most popular way to unit test on the JVM and without support for Realm, it would be hard to make the jump. We are so close to making the decision to switch to Realm, but we definitely need the ability to test outside of a device or the emulator. Thanks.

vvictor10 avatar Feb 19 '15 17:02 vvictor10

I'd like to second this issue, but not with respect to Realm specifically.

My project has a few dependencies (in AAR) that we use to load native libraries. On-device testing/running using these libraries works, but Robolectric testing does not. I have built our native libraries for x86, and loaded them in the AARs, but the java.library.path is wrong. So I continue to get UnsatisfiedLinkError when attempting to load in a Robolectric test.

I can manually set the java.library.path in gradle:

tasks.withType(Test) { systemProperty "java.library.path", '/some/path' }

This helps in gradle runs, but not Android Studio, where the setting is in the unit test configuration. In either case, (gradle on command line or in Android Studio), and even with a hardcoded path to the libraries, Java fails to load them.

daterrell avatar Mar 05 '15 23:03 daterrell

Any progress on this? Just to be clear, if I have an android app with one main activity that uses Realm. It's impossible for me to test the app with Robolectric right now? Or are there any hacks that I can use to mock Realm or ignore the exceptions or anything? Thank you!

nilsi avatar Jun 05 '15 13:06 nilsi

When we have something to share, I'll be sure to update this issue.

The standard way of dealing with external dependencies like this would be to hide the Realm specific stuff behind an interface and use DI to inject a fake implementation in your unit tests.

erd avatar Jun 05 '15 15:06 erd

Great! Do you know any examples where this is shown in practise? I'm having a hard time finding any. Anyway, will try and see if I can get it right.

nilsi avatar Jun 05 '15 15:06 nilsi

What kind of support are you expecting Robolectric to offer / fix?

I'm not familiar with Relm, but it seems to be implemented using native code, therefore, just like any other library that is implemented this way you will either need a version of that native code that runs on desktop computers (OSX/Windows/Linux) or you will need Relm to provide an alternative Java implementation.

Your other options are mocking out Relm, or building an in memory stub.

jongerrish avatar Jun 11 '15 22:06 jongerrish

A good solution is to use dependency injection to use a test implementation of your storage layer when running your tests. You can then use Mockito to mock out your storage implementation and drive your test's behaviour through that.

I admit it's a little bit inconvenient, but it works.

On Fri, Jun 12, 2015 at 10:20 AM, Virl [email protected] wrote:

I dont know. Just make it work!1

Sent from my iPhone

On 12 Jun 2015, at 01:37, Jonathan Gerrish [email protected] wrote:

What kind of support are you expecting Robolectric to offer / fix?

I'm not familiar with Relm, but it seems to be implemented using native code, therefore, just like any other library that is implemented this way you will either need a version of that native code that runs on desktop computers (OSX/Windows/Linux) or you will need Relm to provide an alternative Java implementation.

Your other options are mocking out Relm, or building an in memory stub.

— Reply to this email directly or view it on GitHub.

— Reply to this email directly or view it on GitHub https://github.com/robolectric/robolectric/issues/1389#issuecomment-111409666 .

gcmorrison avatar Jun 12 '15 08:06 gcmorrison

Realm already has x86 support. So we need just set java.library.path.

ypresto avatar Jul 31 '15 10:07 ypresto

Android gradle plugin seems to be not extracting jni *.so files from jar (no lib*.so file found in build/ dir).

ypresto avatar Jul 31 '15 11:07 ypresto

If someone trying to solve this, below extensions might be helpful for extracting so files in jar.

https://github.com/cjstehno/gradle-natives https://github.com/nhachicha/android-native-dependencies

ypresto avatar Jul 31 '15 12:07 ypresto

Related: #1171

ypresto avatar Aug 08 '15 06:08 ypresto

To add to the above comments, I also doubt this can be done (Although there was talk that it previously supported it, Im not sure about that).

Ultimately to correct the no path found exception. On OSX you can copy it to usr/lib/java And rename the file librealm-jni.so - > librealm-jni.jnilib

However it then issues the following similar error, showing the file is now read but not compatible.

java.lang.UnsatisfiedLinkError: /usr/lib/java/librealm-jni.jnilib: dlopen(/usr/lib/java/librealm-jni.jnilib, 1): no suitable image found.  Did find:
    /usr/lib/java/librealm-jni.jnilib: unknown file type, first eight bytes: 0x7F 0x45 0x4C 0x46 0x01 0x01 0x01 0x00
    at java.lang.ClassLoader$NativeLibrary.load(Native Method)

This proves its not a simple solution as some imply. I may give it a go on a linux VM, might have better luck with that (I highly doubt it).

Also android-native-dependencies, appears to require the so files are available outside of the jar/aar. This isnt the case for realm and so shows a 404. The other library also doesn't appear to have any relevance to the actual problem.

Its also worth noting that Jake Wharton provided a clear response regarding this issue. https://github.com/robolectric/robolectric/issues/782

McPo avatar Aug 12 '15 20:08 McPo

Attempted the same only on Ubuntu, result is the same.

OpenJDK 64-Bit Server VM warning: You have loaded library /usr/lib/jni/librealm-jni.so which might have disabled stack guard. The VM will try to fix the stack guard now.
It's highly recommended that you fix the library with 'execstack -c <libfile>', or link it with '-z noexecstack'.

java.lang.UnsatisfiedLinkError: /usr/lib/jni/librealm-jni.so:
/usr/lib/jni/librealm-jni.so: wrong ELF class: ELFCLASS32 (Possible cause: architecture word width mismatch)
        at java.lang.ClassLoader$NativeLibrary.load(Native Method)
        at java.lang.ClassLoader.loadLibrary1(ClassLoader.java:1965)
        at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1890)
        at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1880)
        at java.lang.Runtime.loadLibrary0(Runtime.java:849)
        at java.lang.System.loadLibrary(System.java:1088)
        at io.realm.internal.RealmCore.loadLibrary(RealmCore.java:114)

Have also attempted using execstack, no difference. This was on a Trust 64 bit, will now attempt on 32 bit.

McPo avatar Aug 12 '15 23:08 McPo

Made some progress on a 32 bit ubuntu installation

java.lang.UnsatisfiedLinkError: /usr/lib/jni/librealm-jni.so: liblog.so: cannot open shared object file: No such file or directory
        at java.lang.ClassLoader$NativeLibrary.load(Native Method)
        at java.lang.ClassLoader.loadLibrary1(ClassLoader.java:1965)
        at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1890)
        at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1880)
        at java.lang.Runtime.loadLibrary0(Runtime.java:849)
        at java.lang.System.loadLibrary(System.java:1088)
        at io.realm.internal.RealmCore.loadLibrary(RealmCore.java:114)
        at io.realm.internal.SharedGroup.<clinit>(SharedGroup.java:35)
        at io.realm.Realm.<init>(Realm.java:205)
        at io.realm.Realm.createAndValidate(Realm.java:594)
        at io.realm.Realm.create(Realm.java:564)
        at io.realm.Realm.getInstance(Realm.java:411)
        at io.realm.Realm.getInstance(Realm.java:368)
        at io.realm.Realm.getInstance(Realm.java:349)

I believe it is now complaining about liblog.so. I grabbed liblog.so from https://github.com/sqlcipher/android-database-sqlcipher/blob/master/external/android-libs/x86/liblog.so But no luck same error as above

Anyway more upto date information regarding this issue can be found on real-java repo. https://github.com/realm/realm-java/issues/904

McPo avatar Aug 13 '15 00:08 McPo

Glad you are working on it. It would be very nice to be able to test Realm.

guillermomuntaner avatar Oct 21 '15 11:10 guillermomuntaner

While this is not solved... I got the unity tests running with Robolectric and made a gist to make it easier for anyone trying to test realm:

https://gist.github.com/jturolla/25489e1b676957197201

I'm using Robolectric to test the entire app with Realm mocked. When I need to test some logic that involves the database I'm using the AndroidTestCase.

jturolla avatar Nov 23 '15 14:11 jturolla

I hope this might help someone: https://github.com/niqdev/dagger-realm-test

niqdev avatar Dec 28 '15 11:12 niqdev

The latest version of Realm (0.88.2) breaks the workaround mentioned by @jturolla since it now uses a gradle plugin. This means I cannot update my version of the library because none of my tests will work :(

khayamy001 avatar Mar 18 '16 12:03 khayamy001

Would love to see support for this as I have some apps that are heavily based around Realm.

bachhuberdesign avatar Jun 04 '16 21:06 bachhuberdesign

Is there currently a way to ignore Realm to test other activities/fragments with Roboelectric?

I'm not trying to test anything Realm specific but receive a UnsatisfiedLinkError when building my activities with Roboelectric. Thanks

cgathergood avatar Jun 10 '16 12:06 cgathergood

@cgathergood mock realm, inject a fake realm, etc. that's what dependency injection is for

ZakTaccardi avatar Jun 10 '16 16:06 ZakTaccardi