Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the correct way to check the data from a PagingData object in Android Unit Tests

I am using paging library to retrieve data from an api and show them in a list

for this purpose in my repository I have created the method:

fun getArticleList(query: String): Flow<PagingData<ArticleHeaderData>>

in my viewmodel I have created the search method which goes something like this:

override fun search(query: String) {
    val lastResult = articleFlow
    if (query == lastQuery && lastResult != null)
        return
    lastQuery = query
    searchJob?.cancel()
    searchJob = launch {
        val newResult: Flow<PagingData<ArticleList>> = repo.getArticleList(query)
            .map {
                it.insertSeparators { //code to add separators }.cachedIn(this)
        articleFlow = newResult
        newResult.collectLatest {
            articleList.postValue(it)
        }
    }
}

in order to test my viewmodel I am using the test method PagingData.from to create a flow to return from my mocked repository like so:

whenever(repo.getArticleList(query)).thenReturn(flowOf(PagingData.from(articles)))

and then I retrieve the actual paging data from the articleList LiveData like so:

val data = vm.articleList.value!!

this returns a PagingData<ArticleList> object that I would like to verify it contains the data from the service (i.e. the articles returned by whenever)

the only way I have found to do this is by creating the following extension function:

private val dcb = object : DifferCallback {
    override fun onChanged(position: Int, count: Int) {}
    override fun onInserted(position: Int, count: Int) {}
    override fun onRemoved(position: Int, count: Int) {}
}

suspend fun <T : Any> PagingData<T>.collectData(): List<T> {
    val items = mutableListOf<T>()
    val dif = object : PagingDataDiffer<T>(dcb, TestDispatchers.Immediate) {
        override suspend fun presentNewList(previousList: NullPaddedList<T>, newList: NullPaddedList<T>, newCombinedLoadStates: CombinedLoadStates, lastAccessedIndex: Int): Int? {
            for (idx in 0 until newList.size)
                items.add(newList.getFromStorage(idx))
            return null
        }
    }
    dif.collectFrom(this)
    return items
}

which seems to work, but is based on the PagingDataDiffer class which is marked as @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) so it may not work in the future

is there a better way to either get the flow from PagingData (which is marked as internal in the library) or get the actual data from it?

like image 322
Cruces Avatar asked Aug 21 '20 12:08

Cruces


People also ask

How can I get data from PagingData?

Display the paged data in your UICreate an instance of your PagingDataAdapter class. Pass the PagingDataAdapter instance to the RecyclerView list that you want to display your paged data. Observe the PagingData stream, and pass each generated value to your adapter's submitData() method.

Which API defines the source of data to be retrieved from a single source in paging?

Repository layer Each PagingSource object defines a source of data and how to retrieve data from that source. A PagingSource object can load data from any single source, including network sources and local databases.

What is load Paging?

The Paging library tracks the state of load requests for paged data and exposes it through the LoadState class. Your app can register a listener with the PagingDataAdapter to receive information about the current state and update the UI accordingly.

How do I test my paging system?

Test by dialing – Dial the paging access, or feature code, having your Butt-Sett on speaker-phone will allow you to hear the page through it. if you get busied signals, then check the port designated for paging, and run the maintenance commands. If all fails, then assign a new port, or replace the defective board.


1 Answers

I've had the same problem with the Paging3 library, and there not a lot discussions about this library online yet, but as I digging through some the docs, I may found a solution. The scenario I'm facing is trying to determine whether a data list is empty or not within PagingData, then I'll manipulate the UI base on that.

Here's what I found in the doc, there are two apis in PagingDataAdapter that have been added in version 3.0.0-alpha04 which is peek(), and snapshot(), peek() gives us a specific list object based on index, whereas snapshot() gives us the whole list.

So here's what I've done:

lifecycleScope.launch {
    //Your PagingData flow submits the data to your RecyclerView adapter
    viewModel.allConversations.collectLatest {
        adapter.submitData(it)
    }
}
lifecycleScope.launch {
    //Your adapter's loadStateFlow here
    adapter.loadStateFlow.
        distinctUntilChangedBy {
            it.refresh
        }.collect {
            //you get all the data here
            val list = adapter.snapshot()
            ...
        }
    }

Since I just get my hands on the Paging library and Flow recently, there might be flaws with this approach, let me know if there are better ways!

like image 194
Zijian Wang Avatar answered Sep 22 '22 15:09

Zijian Wang