Sun*_*man 7 android kotlin android-testing android-viewmodel kotlin-coroutines
我正在尝试使用 Kotlin(1.6.21) Coroutines(1.6.4) 和 Kotlin Flow 进行 ViewModel 测试。
遵循官方Kotlin 协程测试文档,但ViewModel 在测试完成之前不会等待/返回挂起函数的结果。已经浏览了 StackOverflow 的热门答案,并尝试了所有建议的解决方案,例如注入相同的内容CoroutineDispatcher,并传递相同的内容CoroutineScope,但到目前为止没有一个有效。所以在这里我发布当前的简单测试实现。必须发布测试用例中涉及的所有类代码以获得更好的想法。
ReferEarnDetailViewModel.kt:
注入 Usecase 和 CoroutineContextProvider 并使用 viewModelScope 和提供的调度程序调用 API。但是从测试用例调用callReferEarnDetails()后,它不会收集模拟用例方法发出的任何数据。已尝试使用直接 repo 方法调用,也没有 Kotlin 流程,但同样失败。
@HiltViewModel class
ReferEarnDetailViewModel @Inject constructor(
val appDatabase: AppDatabase?,
private val referEarnDetailsUseCase: ReferEarnDetailsUseCase,
private val coroutineContextProvider: CoroutineContextProvider) : BaseViewModel() {
fun callReferEarnDetails() {
setProgress(true)
viewModelScope.launch(coroutineContextProvider.default + handler) {
referEarnDetailsUseCase.execute(UrlUtils.getUrl(R.string.url_referral_detail))
.collect { referEarnDetail ->
parseReferEarnDetail(referEarnDetail)
}
}
}
private fun parseReferEarnDetail(referEarnDetail:
ResultState<CommonEntity.CommonResponse<ReferEarnDetailDomain>>) {
when (referEarnDetail) {
is ResultState.Success -> {
setProgress(false)
.....
}
}
}
Run Code Online (Sandbox Code Playgroud)
ReferEarnCodeUseCase.kt:Api 响应的返回流程。
@ViewModelScoped
class ReferEarnCodeUseCase @Inject constructor(private val repository:
IReferEarnRepository) :BaseUseCase {
suspend fun execute(url: String):
Flow<ResultState<CommonEntity.CommonResponse<ReferralCodeDomain>>> {
return repository.getReferralCode(url)
}
}
Run Code Online (Sandbox Code Playgroud)
协程测试规则.kt
@ExperimentalCoroutinesApi
class CoroutineTestRule(val testDispatcher: TestDispatcher =
StandardTestDispatcher()) : TestWatcher() {
val testCoroutineDispatcher = object : CoroutineContextProvider {
override val io: CoroutineDispatcher
get() = testDispatcher
override val default: CoroutineDispatcher
get() = testDispatcher
override val main: CoroutineDispatcher
get() = testDispatcher
}
override fun starting(description: Description?) {
super.starting(description)
Dispatchers.setMain(testDispatcher)
}
override fun finished(description: Description?) {
super.finished(description)
Dispatchers.resetMain()
}
}
Run Code Online (Sandbox Code Playgroud)
ReferEarnDetailViewModelTest.kt
@RunWith(JUnit4::class)
@ExperimentalCoroutinesApi
class ReferEarnDetailViewModelTest {
private lateinit var referEarnDetailViewModel: ReferEarnDetailViewModel
private lateinit var referEarnDetailsUseCase: ReferEarnDetailsUseCase
@get:Rule
val coroutineTestRule = CoroutineTestRule()
@Mock
lateinit var referEarnRepository: IReferEarnRepository
@Mock
lateinit var appDatabase: AppDatabase
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
referEarnDetailsUseCase = ReferEarnDetailsUseCase(referEarnRepository)
referEarnDetailViewModel = ReferEarnDetailViewModel(appDatabase,
referEarnDetailsUseCase , coroutineTestRule.testCoroutineDispatcher)
}
@Test
fun `test api response parsing`() = runTest {
val data = ResultState.Success( TestResponse() )
//When
Mockito.`when`(referEarnDetailsUseCase.execute("")).thenReturn(flowOf(data))
//Call ViewModel function which further call usecase function.
referEarnDetailViewModel.callReferEarnDetails()
//This should be false after API success response but failing here....
assertEquals(referEarnDetailViewModel.showProgress.get(),false)
}
}
Run Code Online (Sandbox Code Playgroud)
已经尝试过这个解决方案:
正如文档中所述,runTest等待其协程中所有启动的完成TestScope(或引发超时)。但它会在退出测试机构时执行此操作。在您的情况下,assertEquals测试体内失败,因此测试立即失败。
一般来说,这种等待所有作业完成的机制是一种防止泄漏的手段,并不适合您的目的。
有两种方法可以控制测试体内协程的执行:
TestDispatcher。| 归档时间: |
|
| 查看次数: |
2160 次 |
| 最近记录: |