Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/198.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 如何从第3页测试分页数据_Android_Integration Testing_Robolectric_Kotlin Flow_Android Paging 3 - Fatal编程技术网

Android 如何从第3页测试分页数据

Android 如何从第3页测试分页数据,android,integration-testing,robolectric,kotlin-flow,android-paging-3,Android,Integration Testing,Robolectric,Kotlin Flow,Android Paging 3,MyViewModel有一个返回分页数据流的方法。在我的应用程序中,数据从远程服务器获取,然后保存到房间(真相的单一来源): 我想知道我是否在正确的轨道上。任何帮助都将不胜感激 基本上有两种方法,这取决于您想要转换前还是转换后的数据 如果您只想断言存储库端,那么您的查询是正确的-您可以直接查询PagingSource,这是预转换,尽管如此,您对ViewModel中的PagingData所做的任何映射或筛选都不会在这里被考虑。但是,如果您想直接测试查询,它更“纯粹” @Test fun repo(

MyViewModel有一个返回分页数据流的方法。在我的应用程序中,数据从远程服务器获取,然后保存到房间(真相的单一来源):


我想知道我是否在正确的轨道上。任何帮助都将不胜感激

基本上有两种方法,这取决于您想要转换前还是转换后的数据

如果您只想断言存储库端,那么您的查询是正确的-您可以直接查询
PagingSource
,这是预转换,尽管如此,您对ViewModel中的PagingData所做的任何映射或筛选都不会在这里被考虑。但是,如果您想直接测试查询,它更“纯粹”

@Test
fun repo() = runBlockingTest {
  val pagingSource = MyPagingSource()
  val loadResult = pagingSource.load(...)
  assertEquals(
    expected = LoadResult.Page(...),
    actual = loadResult,
  )
}
另一种方法是,如果您关心转换,则需要将数据从
PagingData
加载到presenter API中

@Test
fun ui() = runBlockingTest {
  val viewModel = ... // Some AndroidX Test rules can help you here, but also some people choose to do it manually.
  val adapter = MyAdapter(..)

  // You need to launch here because submitData suspends forever while PagingData is alive
  val job = launch {
    viewModel.flow.collectLatest {
      adapter.submitData(it)
    }
  }

  ... // Do some stuff to trigger loads
  advanceUntilIdle() // Let test dispatcher resolve everything

  // How to read from adapter state, there is also .peek() and .itemCount
  assertEquals(..., adapter.snapshot())

  // We need to cancel the launched job as coroutines.test framework checks for leaky jobs
  job.cancel()
}
我发现使用会容易得多

根据您的代码,我认为您的测试用例应该如下所示:

@ExperimentalTime
@ExperimentalCoroutinesApi
@Test
fun `test if receive paged chocolate data`() = runBlockingTest {

    val expected = listOf(
      Chocolate(name = "Dove"),
      Chocolate(name = "Hershey's")
    )

    coEvery {
        dao().getChocolateListData()
    }.returns(
        listOf(
            Chocolate(name = "Dove"),
            Chocolate(name = "Hershey's")
        )
    )

    launchTest {
        viewModel.getChocolates().test(
            timeout = Duration.ZERO,
            validate = {
                val collectedData = expectItem().collectData()
                assertEquals(expected, collectedData)
                expectComplete()
            })
    }
}
我还准备了一个基本ViewModelTest类,用于处理许多设置和拆卸任务:

abstract class BaseViewModelTest {
    @get:Rule
    open val instantTaskExecutorRule = InstantTaskExecutorRule()

    @get:Rule
    open val testCoroutineRule = CoroutineTestRule()

    @MockK
    protected lateinit var owner: LifecycleOwner

    private lateinit var lifecycle: LifecycleRegistry

    @Before
    open fun setup() {
        MockKAnnotations.init(this)

        lifecycle = LifecycleRegistry(owner)
        every { owner.lifecycle } returns lifecycle
    }

    @After
    fun tearDown() {
        clearAllMocks()
    }

    protected fun initCoroutine(vm: BaseViewModel) {
        vm.apply {
            setViewModelScope(testCoroutineRule.testCoroutineScope)
            setCoroutineContext(testCoroutineRule.testCoroutineDispatcher)
        }
    }

    @ExperimentalCoroutinesApi
    protected fun runBlockingTest(block: suspend TestCoroutineScope.() -> Unit) =
        testCoroutineRule.runBlockingTest(block)


    protected fun launchTest(block: suspend TestCoroutineScope.() -> Unit) =
        testCoroutineRule.testCoroutineScope.launch(testCoroutineRule.testCoroutineDispatcher) { block }

}
至于借用的扩展函数
collectData()
(谢谢!!)


还有一个幻灯片介绍了

关于PagingData,您想断言/测试什么?我已经更新了我的问题。PagingData(Paging3版本3.0.0.rc01)、单元测试(JUnit4)谢谢您的回答。在测试Dao查询时,我已经执行了PagingSource查询。我必须在存储库层再次测试它吗?同样,我不想处理存储库测试中的UI,因此我不想采用后一种方法。如果要测试
Pager.flow
的输出,包括任何转换,则需要某种方法来断言
PagingData
的输出。由于整个事件流都是内部的,所以唯一的方法是将其收集到某个presenter API中。这并不一定意味着UI,但您确实需要
PagingDataAdapter
AsyncPagingDataDifference
的实例。更好的测试UTIL取代这些是一个WIP FR,用于将来的分页版本,可能会作为一个单独的模块出现。再次感谢您的回答。我已经更新了我目前的进度。但是,adapter.snapshot()似乎是空的。Dao插入操作是否可以触发加载?我的想法是它可以,因为分页库在后台使用流API。此外,我在哪里可以了解这个WIP FR?提前谢谢。很抱歉,我没有在这里看到你的答复-希望我不会太晚。您正在使用
runBlockingTest
,我看不到从哪里提供
chocolateListAdapter
,但您需要等待实际加载本身,并在执行断言之前让它完成。e、 例如,将
TestCoroutineDispatcher
传递给fetch/main dispatcher并调用
advanceUntilIdle()
testImplementation "app.cash.turbine:turbine:0.2.1"
@ExperimentalTime
@ExperimentalCoroutinesApi
@Test
fun `test if receive paged chocolate data`() = runBlockingTest {

    val expected = listOf(
      Chocolate(name = "Dove"),
      Chocolate(name = "Hershey's")
    )

    coEvery {
        dao().getChocolateListData()
    }.returns(
        listOf(
            Chocolate(name = "Dove"),
            Chocolate(name = "Hershey's")
        )
    )

    launchTest {
        viewModel.getChocolates().test(
            timeout = Duration.ZERO,
            validate = {
                val collectedData = expectItem().collectData()
                assertEquals(expected, collectedData)
                expectComplete()
            })
    }
}
abstract class BaseViewModelTest {
    @get:Rule
    open val instantTaskExecutorRule = InstantTaskExecutorRule()

    @get:Rule
    open val testCoroutineRule = CoroutineTestRule()

    @MockK
    protected lateinit var owner: LifecycleOwner

    private lateinit var lifecycle: LifecycleRegistry

    @Before
    open fun setup() {
        MockKAnnotations.init(this)

        lifecycle = LifecycleRegistry(owner)
        every { owner.lifecycle } returns lifecycle
    }

    @After
    fun tearDown() {
        clearAllMocks()
    }

    protected fun initCoroutine(vm: BaseViewModel) {
        vm.apply {
            setViewModelScope(testCoroutineRule.testCoroutineScope)
            setCoroutineContext(testCoroutineRule.testCoroutineDispatcher)
        }
    }

    @ExperimentalCoroutinesApi
    protected fun runBlockingTest(block: suspend TestCoroutineScope.() -> Unit) =
        testCoroutineRule.runBlockingTest(block)


    protected fun launchTest(block: suspend TestCoroutineScope.() -> Unit) =
        testCoroutineRule.testCoroutineScope.launch(testCoroutineRule.testCoroutineDispatcher) { block }

}