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.
I would like to get loaded RealmResults after results.addChangeListener(listener); is called under the hood of asChangesetObservable.
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
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.
@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();
}
}
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?
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
Maybe it should be awaitCount(2)
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.
Most helpful comment
I suspect you are blocking the looper thread with your
testObserver.awaitCount(1). If that method is callingCountDownLatch.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