android - Kotlin Flow:从不收集 emitAll
问题描述
我正在尝试为 kotlin 版本编写一个 UnitTest,networkBoundResource
它可以在具有多个功能的多个源中找到
这是我的版本,带有以下问题的标记注释。
inline fun <ResultType, RequestType> networkBoundResource(
...
coroutineDispatcher: CoroutineDispatcher
) = flow {
emit(Resource.loading(null)) // emit works!
val data = queryDatabase().firstOrNull()
val flow = if (shouldFetch(data)) {
emit(Resource.loading(data)) // emit works!
try {
saveFetchResult(fetch())
query().map { Resource.success(it) }
} catch (throwable: Throwable) {
onFetchFailed(throwable)
query().map { Resource.error(throwable.toString(), it) }
}
} else {
query().map { Resource.success(it) }
}
emitAll(flow) // emitAll does not work!
}.catch { exception ->
emit(Resource.error("An error occurred while fetching data! $exception", null))
}.flowOn(coroutineDispatcher)
这是我对此代码的单元测试之一。对代码进行了一些编辑以专注于我的问题:
@get:Rule
val testCoroutineRule = TestCoroutineRule()
private val coroutineDispatcher = TestCoroutineDispatcher()
@Test
fun networkBoundResource_noCachedData_shouldMakeNetworkCallAndStoreUserInDatabase() = testCoroutineRule.runBlockingTest {
...
// When getAuthToken is called
val result = networkBoundResource(..., coroutineDispatcher).toList()
result.forEach {
println(it)
}
}
问题是println(it)
只打印Resource.loading(null)
排放量。但是如果你看一下flow {}
块的最后一行,你会发现应该有另一个val flow
. 但是这种排放永远不会到达我的单元测试中。为什么?
解决方案
我不太确定完整的行为,但本质上你想获得一个资源,并且当前的流量都集中在FlowCollector<T>
其中,这使得推理和测试变得更加困难。
我以前从未使用或看过 Google 代码,老实说,我只是看了一眼。我的主要收获是它的封装很差,并且似乎打破了关注点的分离——它管理资源状态,并处理所有 io 工作一类。我希望有 2 个不同的类来分隔该逻辑并允许更轻松的测试。
作为简单的伪代码,我会做这样的事情:
class ResourceRepository {
suspend fun get(r : Request) : Resource {
// abstract implementation details network request and io
// - this function should only fulfill the request
// can now be mocked for testing
delay(3_000)
return Resource.success(Any())
}
}
data class Request(val a : String)
sealed class Resource {
companion object {
val loading : Resource get() = Loading
fun success(a : Any) : Resource = Success(a)
fun error(t: Throwable) : Resource = Error(t)
}
object Loading : Resource()
data class Success(val a : Any) : Resource()
data class Error(val t : Throwable) : Resource()
}
fun resourceFromRequest(r : Request) : Flow<Resource> =
flow { emit(resourceRepository.get(r)) }
.onStart { emit(Resource.loading) }
.catch { emit(Resource.error(it)) }
这使您可以大大简化resourceFromRequest()
函数的实际测试,因为您只需要模拟存储库和一种方法。这使您可以抽象和处理其他地方的网络和 io 工作,这又可以单独进行测试。
推荐阅读
- kotlin - 除非我点击视图,否则 PieChart 不会显示来自 Firebase 的内容
- java - 如何修复空对象引用上的 DroidSpeech 代码?
- php - 如何为选择选项编写触发器?
- javascript - 如何使用 javasript 按日期对表格进行排序?
- java - 表达式的类型必须是数组类型,但它解析为 Instances
- node.js - PassportJS 未对用户进行身份验证
- asynchronous - N 进程障碍问题:在关键代码发出信号之后等待更好吗?
- swift - Xcode 中的 Swift 内存位置 - 字符串摘要
- django - 从 Django 中的表单数据读取 CSV 文件时出现“ValueError: I/O operation on closed file”
- java - 如何在无向图中获取从源到目的地的路径计数(基于非零)?