Realm-java: Testing findAllAsync asChangesetObservable queries with RunTestInLooperThread

Created on 24 May 2018  路  7Comments  路  Source: realm/realm-java


Hi, guys! Thank you for your product. I am trying to write unit-tests for my DAO classes, that uses Realm.
I use RunInLooperThread rule like in your RealmAsyncQueryTests class to test methods with findAllAsync calls. And i got the problem with RealmResults returned during the test.

Goal

I would like to get loaded RealmResults after results.addChangeListener(listener); is called under the hood of asChangesetObservable.

Expected Results

First emit contains not loaded RealmResults, according to emitter.onNext(new CollectionChange<>(results, null)); in changesetsFrom implementation. But second emit should contain loaded RealmResults according to documentation

Actual Results

I got RealmResults with isLoaded = false and it contains my data. RealmResults with isLoaded = true will never be returned from Observable.

E.g. full stack trace with exception
java.lang.AssertionError: Expected: [com.wayray.element.core.data.CarData@0] (class: SingletonList), Actual: [] (latch = 1, values = 0, errors = 0, completions = 0, timeout!)
at io.reactivex.observers.BaseTestConsumer.fail(BaseTestConsumer.java:191)
at io.reactivex.observers.BaseTestConsumer.assertValue(BaseTestConsumer.java:356)
at com.wayray.element.core.dao.CharacteristicDaoTest.test_getUserCharacteristicsByName(CharacteristicDaoTest.java:44)
at java.lang.reflect.Method.invoke(Native Method)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at io.realm.rule.RunInLooperThread$TestThread.run(RunInLooperThread.java:509)

Steps & Code to Reproduce

Describe your current debugging efforts.

Code Sample

@RunWith(AndroidJUnit4.class)
public class CharacteristicDaoTest {
    @Rule
    public final RunInLooperThread looperThread = new RunInLooperThread(new ElementRealmLibraryModule());

    @RunTestInLooperThread
    @Test
    public final void test_getUserCharacteristicsByName() {
        CarData carData = new CarData();
        TestObserver<List<CarData>> testObserver = new TestObserver<>();
        Realm realm = looperThread.getRealm();
        realm.executeTransaction(realm1 -> realm1.copyToRealm(carData));
        realm
                .where(CarData.class)
                .findAllAsync()
                .asChangesetObservable()
                .map(CollectionChange::getCollection)
                .filter(RealmResults::isLoaded)
                .map(realm::copyFromRealm)
                .doOnDispose(realm::close)
                .subscribe(testObserver);
        testObserver.awaitCount(1);
        testObserver.assertValue(Collections.singletonList(carData));
        testObserver.dispose();
        looperThread.testComplete();
    }
}

Version of Realm and tooling

Realm version(s): ?
5.1.0
Realm sync feature enabled: yes/no
no
Android Studio version: ?
3.1.2
Which Android version and device: ?
Emulator with Android P

Could you possibly explain why this test fails, however this code works in the project?

T-Help

Most helpful comment

I suspect you are blocking the looper thread with your testObserver.awaitCount(1). If that method is calling CountDownLatch.await() you are blocking the looper thread from processing any notifications which is why your test times out.

This is also why you almost always see our own tests call looperThread.testComplete() from inside a callback. Example: https://github.com/realm/realm-java/blob/master/realm/realm-library/src/androidTest/java/io/realm/RxJavaTests.java#L188

All 7 comments

Hey, guys! Do you have any idea about my issue? It blocks my from covering my DAO classes with tests(

If i remove .filter(RealmResults::isLoaded) from my test - it succeeds. But i don't want to remove this line in my production code, because this code works other way in application build, rather than from test

Thank you for the answer! But the problem is that Realm does not emit loaded results in the unit-test.
.filter(RealmResults::isLoaded) does not allow my test to be success. And awaitCount with any count parameter fails with timeout.

Theoretically I think it should work which is why I didn't reply (not sure where the Realm official answer is, probably busy working on the incremental transformer support 馃槢 ) anyways, another thing you can try is add realm.refresh() above testObserver.awaitCount(1); although I know that should not be necessary

I suspect you are blocking the looper thread with your testObserver.awaitCount(1). If that method is calling CountDownLatch.await() you are blocking the looper thread from processing any notifications which is why your test times out.

This is also why you almost always see our own tests call looperThread.testComplete() from inside a callback. Example: https://github.com/realm/realm-java/blob/master/realm/realm-library/src/androidTest/java/io/realm/RxJavaTests.java#L188

Thank you very much! You are totally right. After replacing TestObserver with custom Consumer like this:

 CompositeDisposable compositeDisposable = new CompositeDisposable();
        compositeDisposable.add(
                characteristicDao.getUserCharacteristicsByName("userId", "name")
                        .subscribe(characteristicChartValues -> {
                            Assert.assertEquals(characteristicChartValues, Collections.singletonList(characteristicChartValue));
                            compositeDisposable.clear();
                            looperThread.testComplete();
                        }));

my test finished with success.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nolanamy picture nolanamy  路  3Comments

CNyezi picture CNyezi  路  3Comments

Merlin1993 picture Merlin1993  路  3Comments

bryanspano picture bryanspano  路  3Comments

cmelchior picture cmelchior  路  3Comments