如何对Kotlin Flow版本的NetworkBoundResource进行单元测试?
问题根因
你调用first()仅会获取Flow发射的第一个元素就终止订阅,而你的networkBoundResource会在onStart回调中率先发射Resource.loading(null)作为第一个元素,此时订阅直接终止,后续的网络请求、数据库插入逻辑还没执行就被取消了,所以只能验证到数据库查询的调用。
解决步骤
- 先给所有Mock对象添加桩实现,保证流程可以正常走通
- 收集Flow直到业务逻辑执行完成(比如等到拿到成功状态的返回值),不要只取第一个元素
修改后的测试代码示例
@ExperimentalCoroutinesApi class AuthRepositoryTest { companion object { const val FAKE_ID_TOKEN = "FAkE_ID_TOKEN" // 模拟的接口返回用户数据 val FAKE_API_USER = ApiUser(id = 1, name = "test") // 模拟转换后的数据库用户实体 val FAKE_USER = User(id = 1, name = "test") } @get:Rule val testCoroutineRule = TestCoroutineRule() private val coroutineDispatcher = TestCoroutineDispatcher() private val mockUserDao = mockk<UserDao>() private val mockApiService = mockk<TimetrackerApi>() private val sut = AuthRepository( mockUserDao, mockApiService, coroutineDispatcher ) @Test fun getAuthToken_noCachedData_shouldMakeNetworkCallAndSaveToDb() = testCoroutineRule.runBlockingTest { // Given 配置桩实现 // 第一次查询数据库返回空,触发拉取接口;插入后第二次查询返回用户数据 every { mockUserDao.get() } returnsMany listOf(flowOf(null), flowOf(FAKE_USER)) // 模拟接口返回成功 coEvery { mockApiService.getUser(FAKE_ID_TOKEN) } returns NetworkResponse.Success(FAKE_API_USER) // 模拟数据库插入正常执行 coEvery { mockUserDao.insert(any()) } just Runs // When 收集到成功状态的返回值再结束 sut.getAuthToken(FAKE_ID_TOKEN).first { it is Resource.Success } // Then 验证执行顺序 coVerifyOrder { mockUserDao.get() mockApiService.getUser(FAKE_ID_TOKEN) mockUserDao.insert(any()) } } }
补充说明
如果你的shouldFetch逻辑有自定义判断,需要保证第一次查询返回的data触发shouldFetch返回true即可。如果用的是新版协程测试库,把TestCoroutineRule、TestCoroutineDispatcher替换成对应的StandardTestDispatcher、runTest相关API,逻辑完全一致。
内容的提问来源于stack exchange,提问作者muetzenflo
相关产品推荐
相关产品推荐

