robolectric / robolectric

Android Unit Testing Framework
http://robolectric.org
Other
5.87k stars 1.36k forks source link

Support Realm #1389

Open kneth opened 9 years ago

kneth commented 9 years ago

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?

freezy commented 9 years ago

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?

eygraber commented 9 years ago

This would be awesome.

kneth commented 9 years ago

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

freezy commented 9 years ago

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

erd commented 9 years ago

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

freezy commented 9 years ago

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

bmunkholm commented 9 years ago

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?

hanibalsk commented 9 years ago

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

erd commented 9 years ago

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

vvictor10 commented 9 years ago

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

daterrell commented 9 years ago

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.

nilsi commented 9 years ago

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!

erd commented 9 years ago

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.

nilsi commented 9 years ago

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.

jongerrish commented 9 years ago

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.

gcmorrison commented 9 years ago

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 notifications@github.com wrote:

I dont know. Just make it work!1

Sent from my iPhone

On 12 Jun 2015, at 01:37, Jonathan Gerrish notifications@github.com 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 .

ypresto commented 9 years ago

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

ypresto commented 9 years ago

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

ypresto commented 9 years ago

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 commented 9 years ago

Related: #1171

McPo commented 9 years ago

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 commented 9 years ago

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 commented 9 years ago

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

guillermomuntaner commented 8 years ago

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

jturolla commented 8 years ago

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.

niqdev commented 8 years ago

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

khayamy001 commented 8 years ago

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

bachhuberdesign commented 8 years ago

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

cgathergood commented 8 years ago

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

ZakTaccardi commented 8 years ago

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

khayamy001 commented 8 years ago

@cgathergood What I did was subclass my Application class and override the method where I initialize Realm. When testing i just don't load Realm at all and only test my interface layer that speaks to the Realm API's.

mplacona commented 8 years ago

So as per @ZakTaccardi, I decided to take a similar approach to what @niqdev did here. Mocking all the things, but as it turns out, when you update Realm, that approach completely breaks.

The error is something like:

java.lang.UnsatisfiedLinkError: Can't load library: /var/folders/7f/xxctbvmx0ql52kx7rn55hm0mxvqnl9/T/android-tmp-robolectric5412460009663906587/app_lib/librealm-jni.dylib

And this

java.util.ServiceConfigurationError: org.robolectric.internal.ShadowProvider: Provider org.robolectric.shadows.support.v4.Shadows not a subtype

I've looked around, but really can't find anything that is helpful.

Has anyone been able to test Realm Realm 1.1.0? @kneth?

Any thoughts here appreciated

cleemansen commented 8 years ago

Any further progress here? I'm getting the mentioned java.lang.UnsatisfiedLinkError also during mocking all the things.

mplacona commented 8 years ago

Hey @cleemansen have a look at how I solved in here.

niqdev commented 8 years ago

Hi! I've updated my project with Realm v1.x and my working solution for java.lang.UnsatisfiedLinkError was to use a different Application for testing

@Config(application = CustomApplicationTest.class)

The problem was that even if I mocked my DatabaseRealm wrapper class and all Realm methods, it was still injected and databaseRealm.setup() invoked in CustomApplication#onCreate.

apacha commented 7 years ago

Does anyone know, what's the current state of this? It would really be great, if this issue gets fixed soon.

juanmendez commented 7 years ago

I decided to mock Realm with PowerMockito, and so far I've been doing several test for different features. There is more to come. https://github.com/juanmendez/Mocking-Realm. The great thing about Realm is it is very intuitive to mock.

xian commented 7 years ago

I've asked the Realm maintainers if we can do anything to help... https://github.com/realm/realm-java/issues/904#issuecomment-284549524

juanmendez commented 7 years ago

In the event Realm will be testable in the near future with Robolectric, I still want to make my project available for anyone who wants to test in simple unit mode as it's the case right now. Thereafter, I will be glad to help including more features upon request. https://github.com/juanmendez/Mocking-Realm/blob/master/demo/app/src/test/java/info/juanmendez/mockrealmdemo/QueryTests.java

virl commented 7 years ago

Supporting Realm may require fixing Looper support in Robolectric, because, as I understand, Realm heavily relies on them for cross-thread object exchange: #2977, #1993

kneth commented 7 years ago

That's correct. Realm is using Loopers to implement the auto-update and change listener features.

jesuscast commented 7 years ago

I'm not familiar with this problem, but the JNI does not support loading of native libraries from more than one classloader. This has been very problematic with roboelectric testing for me.

Reference: http://docs.oracle.com/javase/7/docs/technotes/guides/jni/jni-12.html#libmanage

easazade commented 5 years ago

add the support for realm please

javierpe commented 3 years ago

Support for realm again please :(

herrbert74 commented 1 year ago

I'm happy to report that realm-kotlin started to work with Robolectric as of 1.10.0-SNAPSHOT. I reported it through their ticketing system, and the latests snapshot started to work with my in-memory Realm tests. Not sure what exactly the fix was, as no ticket is associated with it on Github. It will be released probably in a few days or weeks, if you do not want to use the snapshot version. They also promised to update documentation on it. Maybe they meant this ticket: https://github.com/realm/realm-kotlin/issues/949 Also the change log: https://github.com/realm/realm-kotlin/blob/main/CHANGELOG.md