paging3的单位测试3
#kotlin #android #jetpack

什么是paging3

paging3是一个喷气背包库,它允许我们轻松地从数据源(本地,远程,文件等)加载大型数据集。它逐渐加载数据,减少网络和系统资源的使用情况。它是用Kotlin编写的,并与其他JetPack库进行协调。

依赖性

首先,我们必须将此依赖性添加到我们的 build.gradle

implementation "androidx.paging:paging-runtime-ktx:3.1.1"
implementation "androidx.paging:paging-common-ktx:3.1.1"
testImplementation "io.mockk:mockk:1.12.5"
testImplementation "junit:junit:4.13.2"
testImplementation "io.mockk:mockk:1.12.5"

分页源文件

所以我们有 SpecialationPagingsource 这样:

class SpecializationPagingSource(
    private val professionUid: String,
    private val query: SpecializationQuery,
    private val api: CommonService
) : PagingSource<Int, SpecializationDomain>() {

    companion object {
        private const val STARTING_PAGE = 1
    }

    override fun getRefreshKey(state: PagingState<Int, SpecializationDomain>): Int? {
        return null
    }

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, SpecializationDomain> {
        val currentPage = params.key.takeIf { it != 0 } ?: STARTING_PAGE

        return try {
            val result = withContext(Dispatchers.IO) {
                ApiHandler.handleApi {
                    api.getSpecializationList(professionUid = professionUid, query = query.toMap())
                }
            }

            val totalPages = result?.meta?.pagination?.totalPage ?: 0
            val data = result?.data?.record ?: listOf()

            LoadResult.Page(
                data = data.map { it.toDomain() },
                prevKey = null,
                nextKey = if (currentPage < totalPages) currentPage + 1 else null
            )
        } catch (e: Exception) {
            LoadResult.Error(e)
        }
    }
}

让我们写出测试用例:)

积极的情况

在积极的情况下,我们可以用 pagingsource loadResult.page 返回值。例如:

LoadResult.Page(data = null, prevKey = null, nextKey = null)

我们有2个针对 pagingsource 的测试方案。 刷新附加刷新是当我们的 pagingsource 负载第一页,而 append 是我们的 pagingsource load load load next next pages。

对于刷新测试,我们可以像这样编写测试:

    //given
    val fakeResponse = GeneralResponseWrapper(
        data = GeneralRecordHolder(record = listOf(specializationResponse)),
        meta = GeneralMetaResponse(
            pagination = pagination = GeneralPaginationResponse(
                page = 1,
                totalPage = 1,
                limit = 1,
                totalRecords = 1,
                records = 1
            )
        )
    )
    val expectedResult =
        PagingSource.LoadResult.Page(
            data = listOf(specializationResponse).map { it.toDomain() },
            prevKey = null,
            nextKey = null
        )

    //when
    coEvery {
        mockService.getSpecializationList(any(), any())
    } returns Response.success(fakeResponse)

    //then
    Assert.assertEquals(
        expectedResult,
        specializationPagingSource.load(
            PagingSource.LoadParams.Refresh(
                key = 1,
                loadSize = 1,
                placeholdersEnabled = false
            )
        )
    )

    coVerify {
        mockService.getSpecializationList(any(), any())
    }

请查看通用paginationResponse 类。我们设置 totalpage = 1 ,因为我们只想在第一次分页加载数据时进行测试。然后,我们使用 pagingsource.loadparams.refresh 用于刷新。

对于附加测试,我们可以像这样编写测试:

    //given
    val fakeResponse = GeneralResponseWrapper(
        data = GeneralRecordHolder(record = listOf(specializationResponse)),
        meta = GeneralMetaResponse(
            pagination = GeneralPaginationResponse(
                page = 1,
                totalPage = 2,
                limit = 1,
                totalRecords = 1,
                records = 1
            )
        )
    )

    val expectedResult =
        PagingSource.LoadResult.Page(
            data = listOf(specializationResponse).map { it.toDomain() },
            prevKey = null,
            nextKey = 2
        )

    //when
    coEvery {
        mockService.getSpecializationList(any(), any())
    } returns Response.success(fakeResponse)

    //then
    Assert.assertEquals(
        expectedResult,
        specializationPagingSource.load(
            PagingSource.LoadParams.Append(
                key = 1,
                loadSize = 1,
                placeholdersEnabled = false
            )
        )
    )

    coVerify {
        mockService.getSpecializationList(any(), any())
    }

请查看通用paginationResponse 类。我们设置 totalpage = 2 ,因为我们希望分页上有2页要加载 limit = 1 每个页面。请注意预期的变量。下一个 key = 2 表示我们期望分页负载页。然后,我们使用 pagingsource.loadparams.append 将下一页附加到现有加载数据。

否定案例

在负面情况下,我们可以用 pagingsource loadResult.error 返回值。例如:

PagingSource.LoadResult.Error<Int, T>(Exception("error data"))

,我们的代码看起来像这样:

   //given
    val expectedResult =
        PagingSource.LoadResult.Error<Int, SpecializationDomain>(BadRequestException("error data"))

    //when
    coEvery {
        mockService.getSpecializationList(any(), any())
    } throws expectedResult.throwable

    //then
    Assert.assertEquals(
        expectedResult,
        specializationPagingSource.load(
            PagingSource.LoadParams.Refresh(
                key = 1,
                loadSize = 1,
                placeholdersEnabled = false
            )
        )
    )

    coVerify {
        mockService.getSpecializationList(any(), any())
    }

示例代码:

class SpecializationPagingSource(
    private val professionUid: String,
    private val query: SpecializationQuery,
    private val api: CommonService
) : PagingSource<Int, SpecializationDomain>() {

    companion object {
        private const val STARTING_PAGE = 1
    }

    override fun getRefreshKey(state: PagingState<Int, SpecializationDomain>): Int? {
        return null
    }

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, SpecializationDomain> {
        val currentPage = params.key.takeIf { it != 0 } ?: STARTING_PAGE

        return try {
            val result = withContext(Dispatchers.IO) {
                ApiHandler.handleApi {
                    api.getSpecializationList(professionUid = professionUid, query = query.toMap())
                }
            }

            val totalPages = result?.meta?.pagination?.totalPage ?: 0
            val data = result?.data?.record ?: listOf()

            LoadResult.Page(
                data = data.map { it.toDomain() },
                prevKey = null,
                nextKey = if (currentPage < totalPages) currentPage + 1 else null
            )
        } catch (e: Exception) {
            LoadResult.Error(e)
        }
    }
}
@OptIn(ExperimentalCoroutinesApi::class)
class SpecializationPagingSourceTest : BaseUnitTestDataLayer() {
    lateinit var specializationPagingSource: SpecializationPagingSource

    @MockK
    lateinit var mockService: CommonService

    @RelaxedMockK
    lateinit var specializationQuery: SpecializationQuery

    companion object {
        val specializationResponse = SpecializationResponse(
            "1",
            "dokter hewan"
        )
    }

    @Before
    override fun setUp() {
        super.setUp()
        specializationPagingSource =
            SpecializationPagingSource("thisIsUid", specializationQuery, mockService)
    }

    @Test
    fun `when SpecializationPagingSource refresh return Success`() = runTest {

        //given
        val fakeResponse = GeneralResponseWrapper(
            data = GeneralRecordHolder(record = listOf(specializationResponse)),
            meta = GeneralMetaResponse(
                pagination = pagination = GeneralPaginationResponse(
                    page = 1,
                    totalPage = 2,
                    limit = 1,
                    totalRecords = 1,
                    records = 1
                )
            )
        )
        val expectedResult =
            PagingSource.LoadResult.Page(
                data = listOf(specializationResponse).map { it.toDomain() },
                prevKey = null,
                nextKey = null
            )

        //when
        coEvery {
            mockService.getSpecializationList(any(), any())
        } returns Response.success(fakeResponse)

        //then
        Assert.assertEquals(
            expectedResult,
            specializationPagingSource.load(
                PagingSource.LoadParams.Refresh(
                    key = 1,
                    loadSize = 1,
                    placeholdersEnabled = false
                )
            )
        )

        coVerify {
            mockService.getSpecializationList(any(), any())
        }

    }

    @Test
    fun `when SpecializationPagingSource append return Success`() = runTest {

        //given
        val fakeResponse = GeneralResponseWrapper(
            data = GeneralRecordHolder(record = listOf(specializationResponse)),
            meta = GeneralMetaResponse(
                pagination = GeneralPaginationResponse(
                    page = 1,
                    totalPage = 2,
                    limit = 1,
                    totalRecords = 1,
                    records = 1
                )
            )
        )

        val expectedResult =
            PagingSource.LoadResult.Page(
                data = listOf(specializationResponse).map { it.toDomain() },
                prevKey = null,
                nextKey = 2
            )

        //when
        coEvery {
            mockService.getSpecializationList(any(), any())
        } returns Response.success(fakeResponse)

        //then
        Assert.assertEquals(
            expectedResult,
            specializationPagingSource.load(
                PagingSource.LoadParams.Append(
                    key = 1,
                    loadSize = 1,
                    placeholdersEnabled = false
                )
            )
        )

        coVerify {
            mockService.getSpecializationList(any(), any())
        }

    }

    @Test
    fun `when SpecializationPagingSource return Exception`() = runTest {

        //given
        val expectedResult =
            PagingSource.LoadResult.Error<Int, SpecializationDomain>(BadRequestException("error data"))

        //when
        coEvery {
            mockService.getSpecializationList(any(), any())
        } throws expectedResult.throwable

        //then
        Assert.assertEquals(
            expectedResult,
            specializationPagingSource.load(
                PagingSource.LoadParams.Refresh(
                    key = 1,
                    loadSize = 1,
                    placeholdersEnabled = false
                )
            )
        )

        coVerify {
            mockService.getSpecializationList(any(), any())
        }

    }

}

参考:https://medium.com/@mohamed.gamal.elsayed/android-how-to-test-paging-3-pagingsource-433251ade028