【问题标题】:Instrumented test with Realm failingRealm 失败的仪器化测试
【发布时间】:2016-10-27 09:30:54
【问题描述】:

我正在实施一个仪器测试来测试使用 Realm 的数据库数据源类。所以现在我面临一些关于如何使用固定装置以及如何模拟 Realm 的问题。 我的数据库数据源如下所示:

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();
    }
}

我想在我的模拟领域中有一个联系人列表,以便我可以检查过滤是否正常工作。我怎样才能做到这一点? 我试过这样做:

@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;
        }
    });
}

}

但在执行 Observable.subscribe 时测试失败,但出现以下异常:

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

我能做什么?

提前致谢

【问题讨论】:

    标签: android mockito realm powermock android-testing


    【解决方案1】:

    它在该错误消息中专门告诉您解决问题的方法:

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

    这是因为asObservable() 需要注册一个RealmChangeListener 才能监听Realm 的变化。

    检测线程是非循环线程,这意味着您无法监听其中的变化。

    解决方案,您需要使用 Looper 线程(如主线程),或者创建 Looper 线程,并在该 Looper 线程中创建 Realm 实例。方便的是,RxAndroid 具有所谓的LooperScheduler,您可以使用AndroidSchedulers.from(Looper) 创建它,它允许您在任意循环线程上执行逻辑。

    一种可能性正在研究Realm already tests their looper-related stuff with this RunInLooperThread test rule

    【讨论】:

    【解决方案2】:

    显然,您的 getContacts() 方法在非循环器后台线程上运行,该线程不适用于我们的更改侦听器(因此我们的 asObservable() 方法)。

    您可以只创建 observable,但请记住,它会发出您的列表一次然后完成。对于持续更新,您需要在 Looper 线程上。

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

    【讨论】:

    • 非常感谢您的回答。执行 Observable.just 和 .asObservable() 有什么区别?这种变化意味着什么?
    • asObservable() 是我们更改监听器的包装器。这意味着 observable 永远不会完成,并且会在每次查询结果更改时发出。 Observable.just() 发出一次查询结果并完成。我无法从您的代码中判断这是否可以接受。如果没有,您需要找到一种方法来在 Looper 线程上运行该方法。如果您担心阻塞 UI,您可以改为 query.findAllAsync().asObservable(),它会自动在后台线程上运行查询。
    • 嗨,我遇到了 findAllAsync() 的问题,它总是向我的订阅者返回 null :(
    • 另一方面,我想我不能执行 Observable.just() 因为我想在取消订阅之前收到更改,所以我知道 asObservable() 是必需的
    • 我们的findAllAsync() 方法总是返回一个RealmResults,所以必须有别的东西将它转换为null
    【解决方案3】:

    这是我的一项测试的片段:

    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);
        }
    }
    

    但是,它使用 Dagger2 来创建数据库“数据访问对象”。

    Realm 可以从主线程工作,使用 Observable.toblocking() 这样你的 测试线程会一直等到 jub 完成。

    Realm 使用 Android 的 Handler 进行并发(.map()、.flatmap() 操作符默认返回 Schedulers.computation() 上的结果),所以解决 Handler 问题使用 ApplicationTestCase。

    【讨论】:

    • 感谢您的回答。 ApplicationTestCase 已被弃用,所以我不想使用它。我尝试在订阅前在 observable 处添加 toblocking(),但错误没有改变
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-18
    • 1970-01-01
    相关资源
    最近更新 更多