FVod
FVod

Reputation: 2295

Instrumented test with Realm failing

I'm implementing an instrumented test to test a Database Data Source class that is using Realm. So now I'm facing some problems about how to use fixtures and how to mock Realm. My database data source looks like:

public class DatabaseDataSource {
    private Realm realm;

    public DatabaseDataSource(Realm realm) {
        this.realm = realm;
    }


    public Observable<RealmResults> getContacts(String firstName, String lastName, String city, String zipCode) {

        final RealmQuery realmQuery = realm.where(Contact.class);
        if(!TextUtils.isEmpty(firstName)) {
            realmQuery.contains("firstName", firstName);
        }
        if(!TextUtils.isEmpty(lastName)) {
            realmQuery.contains("lastName", lastName));
        }
        if(!TextUtils.isEmpty(city)) {
            realmQuery.contains("city", city);
        }
        if(!TextUtils.isEmpty(zipCode)) {
            realmQuery.contains("zipCode", zipCode);
        }

        return realmQuery.findAll()
                    .asObservable();
    }
}

I want to have a list of contacts in my mocked realm so I can check that filtering is working fine. How can I do that? I've tried doing:

@RunWith(AndroidJUnit4.class)
public class DatabaseDataSourceTest extends BaseInstrumentedTest{

    private DatabaseDataSource databaseDataSource;

    private List<Contact> contacts;

    @Before
    public void setup() {
        Realm.init(InstrumentationRegistry.getTargetContext());
        Realm.setDefaultConfiguration(new RealmConfiguration.Builder().build());

        databaseDataSource = new DatabaseDataSource(new DatabaseClient());
    }

    @Test
    public void trial() throws Exception {
        subscribeContactsListObservable(databaseDataSource.getContacts("firstName", null, null, null));

        assertEquals(2, contacts.size());

    }

    private void subscribeContactsListObservable(final Observable<RealmResults> observable) {
        notaries = null;
        observable.map(new Func1<RealmResults, List<Contact>>() {
            @Override
            public List<Notary> call(RealmResults realmResults) {
                return realmResults != null? new ArrayList<>(realmResults) : null;
            }
        }).observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Subscriber<List<Contact>>() {
        @Override
        public void onCompleted() {
            contacts = null;
        }

        @Override
        public void onError(Throwable e) {
            contacts = null;
        }

        @Override
        public void onNext(List<Contact> contactsList) {
            contacts = contactsList;
        }
    });
}

}

But the test is failing when doing the Observable.subscribe with the following exception:

You can't register a listener from a non-Looper thread or IntentService thread. 

What may I do?

Thanks in advance

Upvotes: 0

Views: 385

Answers (3)

EpicPandaForce
EpicPandaForce

Reputation: 81578

Well it specifically tells you the solution to your problem in that error message:

You can't register a listener from **a non-Looper thread** or IntentService thread. 

This is because asObservable() needs to register a RealmChangeListener in order to listen to changes in the Realm.

The instrumentation thread is a non-looper thread, so that means you can't listen to changes in it.

Solution, you need to either use a Looper thread (like the main thread), or create a Looper thread, and create the Realm instance in that looper thread. Conveniently, RxAndroid features a so-called LooperScheduler which you can create using AndroidSchedulers.from(Looper), which allows you to execute logic on an arbitrary looper thread.

A possibility is looking into how Realm already tests their looper-related stuff with this RunInLooperThread test rule.

Upvotes: 1

Alex Shutov
Alex Shutov

Reputation: 3282

Here is fragment of one of my tests:

public class PlaybackDatabaseTest extends ApplicationTestCase<Application> {

    public PlaybackDao dao;

    public PlaybackDatabaseTest(){ super(Application.class);}

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        DatabaseModule module = new DatabaseModule();
        RealmConfiguration config = module.providePlaybackRealmConfiguration(getContext());
        dao = module.providePlaybackDatabase(config);
    }


    public void testAddingPlaylistAndDeletingDatabase() {
        dao.purgeDatabase();
        int id1 = 0;
        Playlist playlist = createTestPlaylist(id1, 0, 100);
        dao.addPlaylist(playlist);
        boolean exist1 = dao.isPlaylistExist(String.valueOf(id1));
        assertTrue(exist1);
        dao.purgeDatabase();
        exist1 = dao.isPlaylistExist(String.valueOf(id1));
        assertFalse(exist1);
    }
}

But, it use Dagger2 to create database 'data access object' .

Realm can work from main thread, use Observable.toblocking() so your test thread will wait until jub is done.

Realm use Android's Handler for concurrency (.map(), .flatmap() operators return results on Schedulers.computation() by default), so to solve Handler issue use ApplicationTestCase.

Upvotes: 0

Christian Melchior
Christian Melchior

Reputation: 20126

Apparently your getContacts() methods run on a non-looper background thread which doesn't work with our change listeners (and thus our asObservable() method).

You can just create the observable instead, but keep in mind that it will emit your list once and then complete. For continuous updates you need to be on a Looper thread.

return Observable.just(realmQuery.findAll());

Upvotes: 0

Related Questions