android / architecture-components-samples

Samples for Android Architecture Components.
https://d.android.com/arch
Apache License 2.0
23.4k stars 8.29k forks source link

PagingSample Test Room + Paging3 + Flow different results on each test run #1001

Open bitamirshafiee opened 3 years ago

bitamirshafiee commented 3 years ago

Paging 3, with Room. I have created an app just like the PagingSample and start writing test for it.

Here is what I have in DAO :

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

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

and I wanted to test it like this:

@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?) {}
    }
}

Exactly like the test in the sample. Strange think is that when I run the test several times some is passing and some not(saying the differ.snapshot() is empty) This is happening too when I have several other tests in this file (testing updating and removing) and try to run the whole tests together, some passing and some not which the passing tests are different in each round.

and this is what I have in my ViewModel for getting the list:

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)

I should mention that when I try to test a query like below without the returning type of PagingSource<Int, Model> :

@Query("SELECT * FROM Model")
fun getModels(): List<Model>
everything works fine. the test passes every time I run it. so I think there is something wrong with this part in the test:

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

an it seems it is : differ.submitData(it)

as the docs says: "Caution: The submitData() method suspends and does not return until either the PagingSource is invalidated or the adapter's refresh method is called. This means that code after the submitData() call might execute much later than you intend." So I tried to call differ.refresh() after launch so now every time I run the single test it is ok and is passing but when I tried to run several tests together, again some passing and some not. Will really happy if anyone could help and give some hints, since I am working on it for quite a long time. Thank you.