- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我一直在使用 Google 的 arch 库,但是使测试变得困难的一件事是使用 PagedList
.
对于此示例,我使用存储库模式并从 API 或网络返回详细信息。
所以在 ViewModel 中我调用了这个接口(interface)方法:
override fun getFoos(): Observable<PagedList<Foo>>
然后存储库将使用 RxPagedListBuilder
创建Observable
这是 PagedList 类型:
override fun getFoos(): Observable<PagedList<Foo>> =
RxPagedListBuilder(database.fooDao().selectAll(), PAGED_LIST_CONFIG).buildObservable()
我希望能够为测试设置这些返回 PagedList<Foo>
的方法的返回值.类似于
when(repository.getFoos()).thenReturn(Observable.just(TEST_PAGED_LIST_OF_FOOS)
两个问题:
PagedList<Foo>
?我的目标是以更端到端的方式进行验证(例如确保在屏幕上显示正确的 Foos 列表)。 fragment/Activity/ View 是观察 PagedList<Foo>
的那个来自 ViewModel。
最佳答案
Paging 3 库提供了一个构建器方法 PagingData.from(someList)
.
使用模拟 DataSource.Factory
将列表转换为 PagedList。
@saied89分享了这个solution在这个googlesamples/android-architecture-components问题。我在 Coinverse Open App 中实现了模拟的 PagedList。为了使用 Kotlin、JUnit 5、MockK 和 AssertJ 库对 ViewModel 进行本地单元测试。
为了观察 PagedList 中的 LiveData,我使用了 Jose Alcérreca's implementation来自 LiveDataSample sample app 的 getOrAwaitValue
在 Google 的 Android 架构组件示例下。
asPagedList
扩展函数在下面的示例测试 ContentViewModelTest.kt 中实现。
PagedListTestUtil.kt
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)
}
}
LiveDataTestUtil.kt
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
}
ContentViewModelTest.kt
...
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)
}
}
InstantExecutorExtension.kt
在使用 LiveData 时,这是 JUnit 5 所必需的,以确保观察者不在主线程上。下面是Jeroen Mols' implementation .
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)
}
}
关于android - 如何创建用于测试的对象的 PagedList?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50435770/
设置: 我们正在使用 PagedListEpoxyController , RxPagedListBuilder , ItemKeyedDataSource运行时 Mockgenerator它只是根据
我需要将域对象映射到 UI 对象并使用实时分页列表显示。 我试图映射 LiveData>至 LiveData>和 map PositionalDataSource至 PositionalDataSou
PagedList 已弃用,我应该改用什么?我正在使用分页库从 Firestore 中获取数据。 这是我的代码 val config = PagedList.Config.Builder()
我已经使用 Android 的分页库 (https://developer.android.com/topic/libraries/architecture/paging.html) 实现了一个带有分
我自己手动计算了一堆行,包括一些不在我的数据库中的附加数据。为了提高效率,我只根据页码和页面大小计算了当前页面中的行数。 我试图将其放入 PagedList 中,但这需要整个数据集,而不是我想显示的子
我的问题是如何更新 PagedList 中的项目? 在我的例子中,有 ListActivity 和 DetailsActivity。列表 Activity 使用分页组件从网络(仅)获取帖子,并使用分页
我一直在使用 Google 的 arch 库,但是使测试变得困难的一件事是使用 PagedList . 对于此示例,我使用存储库模式并从 API 或网络返回详细信息。 所以在 ViewModel 中我
我正在尝试将架构组件集成到我的应用程序中,即分页、LiveData、ViewModel。Room 之前已经集成和测试过,所以我可以返回 DataSource.Factory来 self 的 DAO 类
我在我的 View 中使用 PagedList,但我的脚手架 Controller 是使用这种默认索引操作生成的: public async Task Index() { return Vie
我正在使用 http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/sorting-filtering-and-pagin
我丢失了第二页和之后的所有数据。第一个结果/页面显示了正确的数据。第二页及之后,没有数据。 我的代码: public ActionResult Contact(int? pageNumber, str
我正在尝试显示产品列表并显示分页。所以,我决定使用 PagedList 插件。我在下面的链接中阅读了一些有关如何修改 Controller 和 View 的示例。 https://github.com
我安装了 PagedList.MVC Nugget 包,我正在尝试从列表中创建一个 PagedList ListProductsList 在我看来: @model PagedList.IPagedLi
已经两天了,我正在尝试学习如何将新的分页库与 Kotlin 语言一起使用(也是第一次) 所以我已经阅读了很多指南/教程和 Github repo (https://github.com/STAR-ZE
我正在使用 PagedList我的应用程序中的库。 使用 PagedListAdapter 一切正常.但是,我无法找到如何获得回调并收到 PagedList 已更新的通知。 在列表的ItemKeyed
嗨,我正在使用 mvvm 和分页库。分页效果完美。 我想从列表中删除项目。 当我尝试删除项目时 E/AndroidRuntime: FATAL EXCEPTION: main Process:
我正在使用 Play2.0 框架作为后端 API。所以我想从数据库中列出游乐设施,并且我想排除具有重复“地点”名称的游乐设施。 我正在使用此代码,但这给了我存储在数据库中的所有游乐设施记录。如何排除重
我正在使用 TroyGoode 制作的 PagedList:https://github.com/TroyGoode/PagedList/ 但我希望它呈现不同于我目前拥有的其他输出。 默认输出为:ww
我正在尝试找出在 asp.net MVC 中使用 ViewModel 实现分页列表的正确方法。 假设我有以下 PagedClientViewModel: public class PagedClien
我的通用存储库中有以下功能。 var list = query.ToPagedList(pageNumber, pageSizeNumber); 这行有两个问题 public IEnumerable
我是一名优秀的程序员,十分优秀!