Reputation: 2295
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
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
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
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