Android Test Room + Paging 3 + Flow test дает разные результаты при каждом запуске

Страница 3, с номером. Я создал приложение, подобное приведенному в примере здесь, и запускаю написание теста для него.

Вот что у меня есть в DAO:

@Query("SELECT * FROM Model")
fun getModels(): PagingSource<Int, Model>

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(list: MutableList<Model>) : Completable

и я хотел проверить это так:

@OptIn(ExperimentalCoroutinesApi::class) 
class DAOTest {

    private lateinit var dao: Dao
    private lateinit var db: ModelDatabase
    private lateinit var viewModel: MyViewModel
    lateinit var context: Context
    private val testDispatcher = TestCoroutineDispatcher()

    @Before
    fun createDb() {

        Dispatchers.setMain(testDispatcher)
        context = InstrumentationRegistry.getInstrumentation().targetContext
        db = Room.inMemoryDatabaseBuilder(context, MyDatabase::class.java)
            .allowMainThreadQueries()
            .build()
        dao = db.Dao()
        viewModel =
            MyViewModel(MyRepository(db))
    }

    @After
    fun tearDown() {
        Dispatchers.resetMain()
        db.close()
    }

    @Test
    fun dataBase_insertAndGet_success() = runBlockingTest(testDispatcher) {
        val differ = AsyncPagingDataDiffer(
            MyAdapter.diffCallback,
            noopListUpdateCallback,
            testDispatcher,
            testDispatcher
        )
        dao.insertAll(
            mutableListOf(listOfModels)
        ).test().assertResult()

        val job = launch {
            viewModel.getList().collectLatest {
                differ.submitData(it)
            }
        }
        advanceUntilIdle()
        Truth.assertThat(differ.snapshot()).containsExactly(
            model1, model2,model3,model4)
        job.cancel()
    }

    private val noopListUpdateCallback = object : ListUpdateCallback {
        override fun onInserted(position: Int, count: Int) {}
        override fun onRemoved(position: Int, count: Int) {}
        override fun onMoved(fromPosition: Int, toPosition: Int) {}
        override fun onChanged(position: Int, count: Int, payload: Any?) {}
    }
}

Точно так же, как тест в образце. Странно думать, что когда я запускаю тест несколько раз, некоторые из них проходят, а некоторые нет (говоря, что different.snapshot() пуст). Это также происходит, когда у меня есть несколько других тестов в этом файле (обновление и удаление тестирования) и попытка запустите все тесты вместе, некоторые из которых проходят, а некоторые нет, которые проходят разные тесты в каждом раунде.

и это то, что у меня есть в моей ViewModel для получения списка:

fun getList(type: Int, search: String? = null): Flow<PagingData<Stock>> {

        return Pager(
            config = PagingConfig(
                pageSize = PAGE_SIZE,
                enablePlaceholders = true,
                maxSize = MAX_SIZE
            )
        ) {
           repository.getListFromDao()
        }.flow
            .cachedIn(viewModelScope)

все как на образце, но не знаю, почему такое поведение происходит. Я видел другие сообщения, такие как это именно то, что я сделал в своем тесте, но у меня та же проблема. Я должен упомянуть, что когда я пытаюсь протестировать запрос, как показано ниже, без возвращаемого типа PagingSource‹Int, Model›:

@Query("SELECT * FROM Model")
fun getModels(): List<Model>

все работает нормально. тест проходит каждый раз, когда я его запускаю. поэтому я думаю, что с этой частью теста что-то не так:

val job = launch {
    viewModel.getList().collectLatest {
        differ.submitData(it)
    }
}

Буду очень рад, если кто-нибудь сможет помочь и подсказать, так как я работаю над этим довольно долго. Спасибо.


person Bita Mirshafiee    schedule 13.06.2021    source источник
comment
Поможет, если вставить после запуска джоб для submitData? Я не уверен, что пейджинг вовремя обнаружит недействительность, если он будет участвовать в гонках, поскольку вы не используете приостановленную версию.   -  person dlam    schedule 14.06.2021
comment
Спасибо за комментарий. Я так сделал и не работает. Это из документов Android: используйте класс PagingSource напрямую, чтобы использовать сопрограммы Kotlin для асинхронной загрузки. поэтому он использует приостановку под капотом. а в образце запуска задания для Different.submitdata сказано: SubmitData позволяет другим получать данные из PagingData, но приостанавливается до аннулирования, поэтому мы должны запускать это в отдельном задании. думал, может быть, это помогает для выяснения проблем. понятия не имею, что происходит под капотом.   -  person Bita Mirshafiee    schedule 14.06.2021
comment
Как вы сказали, кажется, это из-за различий. как говорится в документах: Внимание: метод submitData() приостанавливается и не возвращается до тех пор, пока либо PagingSource не станет недействительным, либо не будет вызван метод обновления адаптера. Это означает, что код после вызова submitData() может выполниться намного позже, чем вы предполагали. и когда я вызываю Different.refresh() после запуска, когда я запускаю тест несколько раз, все в порядке, но когда я запускаю все тесты вместе, снова некоторые проходят, некоторые нет, но одиночный запуск в порядке. понятия не имею, как заставить отличаться, чтобы дать мне результат, прежде чем утверждать.   -  person Bita Mirshafiee    schedule 14.06.2021


Ответы (1)


Я считаю, что вам нужно дождаться аннулирования, которое запускается через InvalidationTracker с конца комнаты при вставке.

По умолчанию Room использует ArchTaskExecutors, которые вы можете переопределить с помощью RoomDatabase Builder или сделать синхронными с помощью полезного InstantTaskExecutorRule

@get:Rule
val instantRule = InstantTaskExecutorRule()
person dlam    schedule 15.06.2021
comment
О, это кажется действительно разумной причиной отказа, но, к сожалению, это не сработало, как и раньше. - person Bita Mirshafiee; 15.06.2021