Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.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 如何为测试创建对象的页面列表?_Android_Kotlin_Android Testing_Android Architecture Components_Android Paging - Fatal编程技术网

Android 如何为测试创建对象的页面列表?

Android 如何为测试创建对象的页面列表?,android,kotlin,android-testing,android-architecture-components,android-paging,Android,Kotlin,Android Testing,Android Architecture Components,Android Paging,我一直在使用Google的arch库,但有一件事使测试变得困难,那就是使用PagedList 对于本例,我使用存储库模式并从API或网络返回详细信息 因此,在ViewModel中,我调用了这个接口方法: override fun getFoos(): Observable<PagedList<Foo>> 我希望能够通过测试设置这些返回页面列表的方法的返回。类似于 when(repository.getFoos()).thenReturn(Observable.just(

我一直在使用Google的arch库,但有一件事使测试变得困难,那就是使用
PagedList

对于本例,我使用存储库模式并从API或网络返回详细信息

因此,在ViewModel中,我调用了这个接口方法:

override fun getFoos(): Observable<PagedList<Foo>>
我希望能够通过测试设置这些返回
页面列表的方法的返回。类似于

when(repository.getFoos()).thenReturn(Observable.just(TEST_PAGED_LIST_OF_FOOS)
两个问题:

  • 这可能吗
  • 如何创建
    页面列表
  • 我的目标是以更端到端的方式进行验证(例如确保屏幕上显示正确的foo列表)。片段/活动/视图是从ViewModel观察
    页面列表的视图

  • 无法将列表强制转换为页面列表
  • 您不能直接创建页面列表,只能通过数据源创建。一种方法是创建FakeDataSource并返回测试数据 如果是端到端测试,您可以只使用内存数据库。在调用之前添加测试数据。例子:
    实现这一点的简单方法是模拟页面列表。此功能将列表“转换”为页面列表(在这种情况下,我们使用的不是真正的页面列表,而只是模拟版本,如果您需要实现其他页面列表方法,请将它们添加到此功能中)

    页面列表(列表:列表):页面列表{ val pagedList=Mockito.mock(pagedList::class.java)作为pagedList Mockito.`when`(pagedList.get(ArgumentMatchers.anyInt())。然后{invocation-> val index=invocation.arguments.first()作为Int 列表[索引] } Mockito.when`(pagedList.size)。然后返回(list.size) 返回页面列表 }
  • 第3页 分页3库提供了一个生成器方法

    第2页 使用模拟的
    DataSource.Factory
    将列表转换为页面列表

    在本期中分享了这一点。为了使用Kotlin、JUnit 5、MockK和AssertJ库对ViewModel进行本地单元测试,我在中实现了mocked PagedList

    为了观察页面列表中的实时数据,我使用了谷歌Android架构组件示例中的
    getOrAwaitValue

    asPagedList
    扩展函数在下面的示例测试内容viewmodeltest.kt中实现

    PagedListTestUtil.kt

    InstantExecutorExtension.kt

    在使用LiveData时,JUnit5需要这样做,以确保观察者不在主线程上。下面是


    我发现您的问题中缺少的是:您希望在测试中进行什么验证?@arekolek谢谢您的反馈…我用我试图验证的内容更新了问题@isuPatches是否更新了您实施的策略?我正在用Kotlin、JUnit5和Mock编写相同的测试。我很高兴在这里分享我的解决方案,一旦我得到了概念验证。别忘了先添加Mockito:
    testImplementation“org.Mockito:Mockito core:2.25.0”
    你能将
    列表
    投射到
    页面列表
    @bsobat?@bsobat,这应该返回一个填充了数据的
    页面列表
    ,还是返回一个空的
    页面列表
    ,以避免测试失败?如果为空,它将无法用于测试应用程序如何处理
    页面列表
    数据。@Hurwitz不,您不能cast@bsobat,查看上面使用MockK实现在编写本地JUnit 5测试时使用PagedList的行为。感谢您的深入了解!我有一个模拟页面列表,在本地JUnit 5测试中使用MockK。它是否与分页3库类似?好问题@Raghunandan!我还没有测试分页3库。如果您实施并发现更改,请在帖子上添加评论/编辑。
    when(repository.getFoos()).thenReturn(Observable.just(TEST_PAGED_LIST_OF_FOOS)
    
     fun <T> mockPagedList(list: List<T>): PagedList<T> {
         val pagedList = Mockito.mock(PagedList::class.java) as PagedList<T>
         Mockito.`when`(pagedList.get(ArgumentMatchers.anyInt())).then { invocation ->
            val index = invocation.arguments.first() as Int
            list[index]
         }
         Mockito.`when`(pagedList.size).thenReturn(list.size)
         return pagedList
     }
    
    
        import android.database.Cursor
        import androidx.paging.DataSource
        import androidx.paging.LivePagedListBuilder
        import androidx.paging.PagedList
        import androidx.room.RoomDatabase
        import androidx.room.RoomSQLiteQuery
        import androidx.room.paging.LimitOffsetDataSource
        import io.mockk.every
        import io.mockk.mockk
    
        fun <T> List<T>.asPagedList() = LivePagedListBuilder<Int, T>(createMockDataSourceFactory(this),
            Config(enablePlaceholders = false,
                    prefetchDistance = 24,
                    pageSize = if (size == 0) 1 else size))
            .build().getOrAwaitValue()
    
        private fun <T> createMockDataSourceFactory(itemList: List<T>): DataSource.Factory<Int, T> =
            object : DataSource.Factory<Int, T>() {
                override fun create(): DataSource<Int, T> = MockLimitDataSource(itemList)
            }
    
        private val mockQuery = mockk<RoomSQLiteQuery> {
            every { sql } returns ""
        }
    
        private val mockDb = mockk<RoomDatabase> {
            every { invalidationTracker } returns mockk(relaxUnitFun = true)
        }
    
        class MockLimitDataSource<T>(private val itemList: List<T>) : LimitOffsetDataSource<T>(mockDb, mockQuery, false, null) {
            override fun convertRows(cursor: Cursor?): MutableList<T> = itemList.toMutableList()
            override fun countItems(): Int = itemList.count()
            override fun isInvalid(): Boolean = false
            override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback<T>) { /* Not implemented */ }
    
            override fun loadRange(startPosition: Int, loadCount: Int) =
                itemList.subList(startPosition, startPosition + loadCount).toMutableList()
    
            override fun loadInitial(params: LoadInitialParams, callback: LoadInitialCallback<T>) {
                callback.onResult(itemList, 0)
            }
        }
    
    
        import androidx.lifecycle.LiveData
        import androidx.lifecycle.Observer
        import java.util.concurrent.CountDownLatch
        import java.util.concurrent.TimeUnit
        import java.util.concurrent.TimeoutException
    
        /**
         * Gets the value of a [LiveData] or waits for it to have one, with a timeout.
         *
         * Use this extension from host-side (JVM) tests. It's recommended to use it alongside
         * `InstantTaskExecutorRule` or a similar mechanism to execute tasks synchronously.
         */
        fun <T> LiveData<T>.getOrAwaitValue(
            time: Long = 2,
            timeUnit: TimeUnit = TimeUnit.SECONDS,
            afterObserve: () -> Unit = {}
        ): T {
            var data: T? = null
            val latch = CountDownLatch(1)
            val observer = object : Observer<T> {
                override fun onChanged(o: T?) {
                    data = o
                    latch.countDown()
                    this@getOrAwaitValue.removeObserver(this)
                }
            }
            this.observeForever(observer)
            afterObserve.invoke()
            // Don't wait indefinitely if the LiveData is not set.
            if (!latch.await(time, timeUnit)) {
                this.removeObserver(observer)
                throw TimeoutException("LiveData value was never set.")
            }
            @Suppress("UNCHECKED_CAST")
            return data as T
        }
    
        ...
        import androidx.paging.PagedList
        import com.google.firebase.Timestamp
        import io.mockk.*
        import org.assertj.core.api.Assertions.assertThat
        import org.junit.jupiter.api.AfterAll
        import org.junit.jupiter.api.BeforeAll
        import org.junit.jupiter.api.BeforeEach
        import org.junit.jupiter.api.Test
        import org.junit.jupiter.api.extension.ExtendWith
    
        @ExtendWith(InstantExecutorExtension::class)
        class ContentViewModelTest {
            val timestamp = getTimeframe(DAY)
    
            @BeforeAll
            fun beforeAll() {
                mockkObject(ContentRepository)
            }
    
            @BeforeEach
            fun beforeEach() {
                clearAllMocks()
            }
    
            @AfterAll
            fun afterAll() {
                unmockkAll()
            }
    
            @Test
            fun `Feed Load`() {
                val content = Content("85", 0.0, Enums.ContentType.NONE, Timestamp.now(), "",
                    "", "", "", "", "", "", MAIN,
                    0, 0.0, 0.0, 0.0, 0.0,
                    0.0, 0.0, 0.0, 0.0)
                every {
                    getMainFeedList(any(), any())
                } returns liveData { 
                   emit(Lce.Content(
                       ContentResult.PagedListResult(
                            pagedList = liveData {emit(listOf(content).asPagedList())}, 
                            errorMessage = ""))
                }
                val contentViewModel = ContentViewModel(ContentRepository)
                contentViewModel.processEvent(ContentViewEvent.FeedLoad(MAIN, DAY, timestamp, false))
                assertThat(contentViewModel.feedViewState.getOrAwaitValue().contentList.getOrAwaitValue()[0])
                    .isEqualTo(content)
                assertThat(contentViewModel.feedViewState.getOrAwaitValue().toolbar).isEqualTo(
                    ToolbarState(
                            visibility = GONE,
                            titleRes = app_name,
                            isSupportActionBarEnabled = false))
                verify {
                    getMainFeedList(any(), any())
                }
                confirmVerified(ContentRepository)
            }
        }
    
        import androidx.arch.core.executor.ArchTaskExecutor
        import androidx.arch.core.executor.TaskExecutor
        import org.junit.jupiter.api.extension.AfterEachCallback
        import org.junit.jupiter.api.extension.BeforeEachCallback
        import org.junit.jupiter.api.extension.ExtensionContext
    
        class InstantExecutorExtension : BeforeEachCallback, AfterEachCallback {
            override fun beforeEach(context: ExtensionContext?) {
                ArchTaskExecutor.getInstance().setDelegate(object : TaskExecutor() {
                    override fun executeOnDiskIO(runnable: Runnable) = runnable.run()
                    override fun postToMainThread(runnable: Runnable) = runnable.run()
                    override fun isMainThread(): Boolean = true
                })
            }
    
            override fun afterEach(context: ExtensionContext?) {
                ArchTaskExecutor.getInstance().setDelegate(null)
            }
        }