Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/195.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 如何对Paging3(PagingSource)进行单元测试?_Android_Android Paging Library - Fatal编程技术网

Android 如何对Paging3(PagingSource)进行单元测试?

Android 如何对Paging3(PagingSource)进行单元测试?,android,android-paging-library,Android,Android Paging Library,谷歌最近宣布了新的分页3库,Kotlin first库,支持协同路由和流…等等 我玩过他们提供的,但似乎还并没有任何测试支持,我也检查过。他们没有提到任何关于测试的内容,因此,例如,我想对这个PagingSource进行单元测试: class GithubPagingSource(private val service: GithubService, private val query: String) : PagingSource<Int, R

谷歌最近宣布了新的分页3库,Kotlin first库,支持协同路由和流…等等

我玩过他们提供的,但似乎还并没有任何测试支持,我也检查过。他们没有提到任何关于测试的内容,因此,例如,我想对这个PagingSource进行单元测试:

 class GithubPagingSource(private val service: GithubService,
                     private val query: String) : PagingSource<Int, Repo>() {

override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Repo> {
    //params.key is null in loading first page in that case we would use constant GITHUB_STARTING_PAGE_INDEX
    val position = params.key ?: GITHUB_STARTING_PAGE_INDEX
    val apiQuery = query + IN_QUALIFIER
    return try {
        val response = service.searchRepos(apiQuery, position, params.loadSize)
        val data = response.items
        LoadResult.Page(
                        data,
                        if (position == GITHUB_STARTING_PAGE_INDEX) null else position - 1,
                        if (data.isEmpty()) null else position + 1)
    }catch (IOEx: IOException){
        Log.d("GithubPagingSource", "Failed to load pages, IO Exception: ${IOEx.message}")
        LoadResult.Error(IOEx)
    }catch (httpEx: HttpException){
        Log.d("GithubPagingSource", "Failed to load pages, http Exception code: ${httpEx.code()}")
        LoadResult.Error(httpEx)
    }
  }
}  
class GithubPagingSource(私有val服务:GithubService,
private val查询:String):PagingSource(){
覆盖挂起乐趣加载(参数:LoadParams):LoadResult{
//在加载第一页时params.key为null,在这种情况下,我们将使用常量GITHUB\u start\u page\u索引
val position=params.key?:GITHUB\u开始\u页面\u索引
val apiQuery=query+IN_限定符
回击{
val响应=service.searchRepos(apiQuery、position、params.loadSize)
val数据=响应.items
LoadResult.Page(
数据,
如果(position==GITHUB\u start\u PAGE\u INDEX)为null,则位置为-1,
if(data.isEmpty())null else位置+1)
}捕获(IOEx:IOException){
Log.d(“gitubpagingsource”,“加载页面失败,IO异常:${IOEx.message}”)
LoadResult.Error(IOEx)
}捕获(httpEx:HttpException){
Log.d(“GithubPagingSource”,“加载页面失败,http异常代码:${httpEx.code()}”)
LoadResult.Error(httpEx)
}
}
}  
那么,我如何测试这一点,有人能帮我吗???

Kotlin协同流程 您可以在测试运行之前和之后使用JUnit本地测试。然后,调用发出
paginsource
的Kotlin流的方法,在本地测试环境中观察结果数据,并与预期结果进行比较

JUnit5测试扩展不是必需的。调度器只需要在每次测试前后设置和清除,以便观察测试环境中与Android系统上的协同路由

@ExperimentalCoroutinesApi
class FeedViewTestExtension : BeforeEachCallback, AfterEachCallback, ParameterResolver {

    override fun beforeEach(context: ExtensionContext?) {
        // Set TestCoroutineDispatcher.
        Dispatchers.setMain(context?.root
                ?.getStore(TEST_COROUTINE_DISPATCHER_NAMESPACE)
                ?.get(TEST_COROUTINE_DISPATCHER_KEY, TestCoroutineDispatcher::class.java)!!)
    }

    override fun afterEach(context: ExtensionContext?) {
        // Reset TestCoroutineDispatcher.
        Dispatchers.resetMain()
        context?.root
                ?.getStore(TEST_COROUTINE_DISPATCHER_NAMESPACE)
                ?.get(TEST_COROUTINE_DISPATCHER_KEY, TestCoroutineDispatcher::class.java)!!
                .cleanupTestCoroutines()
        context.root
                ?.getStore(TEST_COROUTINE_SCOPE_NAMESPACE)
                ?.get(TEST_COROUTINE_SCOPE_KEY, TestCoroutineScope::class.java)!!
                .cleanupTestCoroutines()
    }

    ...
}
您可以在页面2中的app/src/test/java/app/coinverse/feedViewModel/FeedViewTest下看到本地JUnit 5测试


Paging 3的不同之处在于,您不需要设置LiveData executor,因为Kotlin Flow可以返回
PagingData

,我目前也有类似的经验,发现分页库并非真正设计为可测试的。我相信,一旦它成为一个更成熟的库,谷歌将使它更易于测试

我能够为
PagingSource
编写一个测试。我使用了rxjava3插件和,但是测试的总体思路应该可以在协同程序版本的API和大多数测试框架中重现

class ItemPagingSourceTest {

    private val itemList = listOf(
            Item(id = "1"),
            Item(id = "2"),
            Item(id = "3")
    )

    private lateinit var source: ItemPagingSource

    private val service: ItemService = mock()

    @Before
    fun `set up`() {
        source = ItemPagingSource(service)
    }

    @Test
    fun `getItems - should delegate to service`() {
        val onSuccess: Consumer<LoadResult<Int, Item>> = mock()
        val onError: Consumer<Throwable> = mock()
        val params: LoadParams<Int> = mock()

        whenever(service.getItems(1)).thenReturn(Single.just(itemList))
        source.loadSingle(params).subscribe(onSuccess, onError)

        verify(service).getItems(1)
        verify(onSuccess).accept(LoadResult.Page(itemList, null, 2))
        verifyZeroInteractions(onError)
    }
}
class ItemPagingSourceTest{
private val itemList=listOf(
项目(id=“1”),
项目(id=“2”),
项目(id=“3”)
)
私有lateinit变量源:ItemPagingSource
私有val服务:ItemService=mock()
@以前
有趣的“设置”(){
source=项目分页源(服务)
}
@试验
fun`getItems-应委托给服务`(){
val onSuccess:Consumer=mock()
val onError:Consumer=mock()
val params:LoadParams=mock()
无论何时(service.getItems(1))。然后返回(Single.just(itemList))
source.loadSingle(params.subscribe)(onSuccess,onError)
验证(服务)。获取项目(1)
验证(onSuccess).accept(LoadResult.Page(itemList,null,2))
验证零交互(onError)
}
}

它并不完美,因为
verify(onSuccess).accept(LoadResult.Page(itemList,null,2))
依赖于
LoadResult.Page
作为一个
数据类,可以通过其属性值进行比较。但是它确实测试了分页源码,我有解决方案,但我认为这不是分页测试的好主意。我对分页v3的所有测试都是在进行仪表测试,而不是本地单元测试,这是因为如果我在本地测试中使用相同的方法(也使用robolectrict),它仍然不起作用

这是我的测试用例,我使用mockwebserver模拟并计算网络请求,该请求必须等于我的预期值

@RunWith(AndroidJUnit4::class)
@SmallTest
class SearchMoviePagingTest {
    private lateinit var recyclerView: RecyclerView
    private val query = "A"
    private val totalPage = 4

    private val service: ApiService by lazy {
        Retrofit.Builder()
                .baseUrl("http://localhost:8080")
                .addConverterFactory(GsonConverterFactory.create())
                .build().create(ApiService::class.java)
    }

    private val mappingCountCallHandler: HashMap<Int, Int> = HashMap<Int, Int>().apply {
        for (i in 0..totalPage) {
            this[i] = 0
        }
    }

    private val adapter: RecyclerTestAdapter<MovieItemResponse> by lazy {
        RecyclerTestAdapter()
    }

    private lateinit var pager: Flow<PagingData<MovieItemResponse>>

    private lateinit var mockWebServer: MockWebServer

    private val context: Context
        get() {
            return InstrumentationRegistry.getInstrumentation().targetContext
        }

    @Before
    fun setup() {
        mockWebServer = MockWebServer()
        mockWebServer.start(8080)

        recyclerView = RecyclerView(context)
        recyclerView.adapter = adapter

        mockWebServer.dispatcher = SearchMoviePagingDispatcher(context, ::receiveCallback)
        pager = Pager(
                config = PagingConfig(
                        pageSize = 20,
                        prefetchDistance = 3, // distance backward to get pages
                        enablePlaceholders = false,
                        initialLoadSize = 20
                ),
                pagingSourceFactory = { SearchMoviePagingSource(service, query) }
        ).flow
    }

    @After
    fun tearDown() {
        mockWebServer.dispatcher.shutdown()
        mockWebServer.shutdown()
    }

    @Test
    fun should_success_get_data_and_not_retrieve_anymore_page_if_not_reached_treshold() {
        runBlocking {
            val job = executeLaunch(this)
            delay(1000)
            adapter.forcePrefetch(10)
            delay(1000)

            Assert.assertEquals(1, mappingCountCallHandler[1])
            Assert.assertEquals(0, mappingCountCallHandler[2])
            Assert.assertEquals(20, adapter.itemCount)
            job.cancel()
        }
    }

....
    private fun executeLaunch(coroutineScope: CoroutineScope) = coroutineScope.launch {
        val res = pager.cachedIn(this)
        res.collectLatest {
            adapter.submitData(it)
        }
    }

    private fun receiveCallback(reqPage: Int) {
        val prev = mappingCountCallHandler[reqPage]!!
        mappingCountCallHandler[reqPage] = prev + 1
    }
}
@RunWith(AndroidJUnit4::class)
@小测验
类SearchMoviePagingTest{
私有lateinit var recyclerView:recyclerView
private val query=“A”
private val totalPage=4
私有val服务:由lazy提供的ApiService{
改装.Builder()
.baseUrl(“http://localhost:8080")
.addConverterFactory(GsonConverterFactory.create())
.build().create(ApiService::class.java)
}
私有val mappingCountCallHandler:HashMap=HashMap().apply{
对于(0..totalPage中的i){
这个[i]=0
}
}
私有val适配器:lazy提供的RecyclerTestAdapter{
回收站适配器()
}
私有lateinit变量分页器:流
私有lateinit var mockWebServer:mockWebServer
私有val上下文:上下文
得到(){
返回InstrumentationRegistry.getInstrumentation().targetContext
}
@以前
趣味设置(){
mockWebServer=mockWebServer()
mockWebServer.start(8080)
recyclerView=recyclerView(上下文)
recyclerView.adapter=适配器
mockWebServer.dispatcher=SearchMoviePagingDispatcher(上下文:::receiveCallback)
寻呼机(
config=PagingConfig(
pageSize=20,
prefetchDistance=3,//向后获取页面的距离
enablePlaceholders=false,
initialLoadSize=20
),
pagingSourceFactory={SearchMoviePagingSource(服务,查询)}
).流动
}
@之后
有趣的撕裂{
mockWebServer.dispatcher.shutdown()文件
mockWebServer.shutdown()
}
@试验
乐趣应该是成功获取数据,不再检索页面{