使用远程中介和分页源对分页 3 的存储库进行单元测试 [英] Unit testing a repository with paging 3 using a a remote mediator and paging source

查看:93
本文介绍了使用远程中介和分页源对分页 3 的存储库进行单元测试的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试对 android 中的存储库类进行单元测试,该类使用带有远程中介和分页源的分页.

I am trying to unit test a repository class in android which is using paging with a remote mediator and paging source.

但是当我运行测试时返回的结果是空的,虽然实际应该包含测试项.

But when I run the test the returned result is empty, although actual should contain the test item.

如下:

这是我的仓库

class PostsRepository @Inject constructor(
    private val postsApi: AutomatticPostsApi,
    private val postsDao: PostDao
) : IPostsRepository {

    @ExperimentalPagingApi
    override fun loadPosts(): Flow<PagingData<Post>> {
        println("loadPosts")
        return Pager(
            config = PagingConfig(20),
            initialKey = 1,
            remoteMediator = PostsPageRemoteMediator(
                postsApi,
                postsDao
            ),
            pagingSourceFactory = { postsDao.getPostsPagingSource() }
        ).flow.map { pagingData ->
            pagingData.map { it.toPost() }
        }
    }

}

这是我的 UT

@ExperimentalCoroutinesApi
@ExperimentalPagingApi
class PostsRepositoryTest {
    @get:Rule
    val instantTaskExecutorRule = InstantTaskExecutorRule()

    private val coroutineDispatcher = TestCoroutineDispatcher()
    private lateinit var postDao: FakePostDao
    private lateinit var postsApi: CommonAutomatticPostsApi
    private val remotePosts = listOf(createDummyPostResponse())
    private val domainPosts = remotePosts.map { it.toPost() }

    //GIVEN: subject under test
    private lateinit var postsRepository: PostsRepository

    @Before
    fun createRepository() =  coroutineDispatcher.runBlockingTest {
        postsApi = CommonAutomatticPostsApi(remotePosts.toMutableList())
        postDao = FakePostDao()
        postsRepository = PostsRepository(postsApi, postDao)
    }

    @Test
    fun loadPosts_returnsCorrectPosts() = runBlockingTest {
        //WHEN: posts are retrieved from paging source

        launch {

            postsRepository.loadPosts().collect { pagingData ->

                val posts = mutableListOf<Post>()
                pagingData.map {

                    posts.add(it)
                    println(it)
                }

                //THEN: retrieved posts should be the remotePosts
                assertThat(posts, IsEqual(domainPosts))
            }

        }

    }
}

这里是 FakeApi、FakePagingSource 和 FakeDao

class CommonAutomatticPostsApi(val posts: MutableList<PostResponse> = mutableListOf()) : AutomatticPostsApi {
    companion object {
        const val SUBSCRIBER_COUNT = 2L
        const val AUTHOR_NAME = "RR"
    }

    override suspend fun loadPosts(page: Int, itemCount: Int): PostsResponse {
        println("Loaded")
        return PostsResponse(posts.size.toLong(), posts)
    }
}

class FakePostsPagingSource() : PagingSource<Int, PostEntity>() {
    var triggerError = false
    var posts: List<PostEntity> = emptyList()
        set(value) {
            println("set")
            field = value
            invalidate()
        }

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, PostEntity> {
        println("load")
        if (triggerError) {
            return LoadResult.Error(Exception("A test error triggered"))
        }
        println("not error")

        return LoadResult.Page(
            data = posts,
            prevKey = null,
            nextKey = null
        )
    }

    override fun getRefreshKey(state: PagingState<Int, PostEntity>): Int? {
        println("refresh")

        return state.anchorPosition ?: 1
    }
}

class FakePostDao(val posts: MutableList<PostEntity> = mutableListOf()) : PostDao {
    val pagingSource = FakePostsPagingSource()

    override suspend fun insertPosts(posts: List<PostEntity>) {
        this.posts.addAll(posts)
        println("insertPosts")
        updatePagingSource()
    }

    override suspend fun updatePost(post: PostEntity) {
        onValidPost(post.id) {
            posts[it] = post
            updatePagingSource()
        }
    }

    private fun onValidPost(postId: Long, block: (index: Int) -> Unit): Boolean {
        println("onValidPost")

        val index = posts.indexOfFirst { it.id == postId }
        if (index != -1) {
            block(index)
            return true
        }

        return false
    }

    override suspend fun updatePost(postId: Long, subscriberCount: Long) {
        onValidPost(postId) {
            posts[it] = posts[it].copy(subscriberCount = subscriberCount)
            updatePagingSource()
        }
    }

    override suspend fun getPostById(postId: Long): PostEntity? {
        val index = posts.indexOfFirst { it.id == postId }
        return if (index != -1) {
            posts[index]
        } else {
            null
        }
    }

    override suspend fun getPosts(): List<PostEntity> {
        println("getPosts")

        return posts
    }

    override fun getPostsPagingSource(): PagingSource<Int, PostEntity> {
        println("getPostsPagingSource")

        return pagingSource
    }

    override suspend fun clearAll() {
        posts.clear()
        updatePagingSource()
    }

    private fun updatePagingSource() {
        println("updatePagingSource")

        pagingSource.posts = posts
    }

    @Transaction
    override suspend fun refreshPosts(newPosts: List<PostEntity>) {
        println("refreshPosts")
        clearAll()
        insertPosts(newPosts)
    }
}

推荐答案

PagingData 测试 (kotlin),版本:3.0.0-rc01.感谢法里德.

PagingData test (kotlin), version: 3.0.0-rc01. Thanks to Farid.

   private suspend fun <T : Any> PagingData<T>.collectDataForTest(): List<T> {
        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) {}
        }
        val items = mutableListOf<T>()
        val dif = object : PagingDataDiffer<T>(dcb, TestCoroutineDispatcher()) {
            override suspend fun presentNewList(
                previousList: NullPaddedList<T>,
                newList: NullPaddedList<T>,
                newCombinedLoadStates: CombinedLoadStates,
                lastAccessedIndex: Int,
                onListPresentable: () -> Unit
            ): Int? {
                for (idx in 0 until newList.size)
                    items.add(newList.getFromStorage(idx))
                onListPresentable()
                return null
            }
        }
        dif.collectFrom(this)
        return items
    }

用法:

    // searchHistoryList: Flow<PagingData<Your Data type>>
    val tmp = useCase.searchHistoryList.take(1).toList().first()
    // result: List<Your Data type>
    val result = tmp.collectDataForTest()
    assertEquals(expect, result)

这篇关于使用远程中介和分页源对分页 3 的存储库进行单元测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆