![](/img/trans.png)
[英]Android: ViewModel test using kotlin coroutines not working
[英]Android ViewModel Coroutines launch Test not waiting
我正在嘗試使用 Kotlin(1.6.21) Coroutines(1.6.4) 和 Kotlin Flow 進行 ViewModel 測試。
遵循官方Kotlin 協程測試文檔,但ViewModel 在測試完成之前不會等待/返回暫停功能的結果。 已經瀏覽了 StackOverflow 的頂級答案,並嘗試了所有建議的解決方案,例如注入相同的CoroutineDispatcher
和傳遞相同的CoroutineScope
但到目前為止都沒有。 所以在這里我發布當前的簡單測試實現。 必須發布測試用例中涉及的所有類代碼以獲得更好的想法。
參考EarnDetailViewModel.kt:
注入 Usecase 和 CoroutineContextProvider 並使用 viewModelScope 和提供的調度程序調用 API。 但是在從測試用例調用callReferEarnDetails()之后,它不會收集模擬用例方法發出的任何數據。 已嘗試使用直接回購方法調用,也沒有 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)
.....
}
}
}
CoroutineTestRule.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()
}
}
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)
}
}
CoroutineTestRule.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()
}
}
參考EarnDetailViewModelTest.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)
}
}
試過這個解決方案:
您是否在advanceUntilIdle()
之后嘗試了 advanceUntilIdle referEarnDetailViewModel.callReferEarnDetails()
?
正如文檔中所述, runTest等待其TestScope
協程中所有啟動的完成(或引發超時)。 但它會在退出測試機構時這樣做。 在您的情況下, assertEquals
在測試主體內失敗,因此測試立即失敗。
一般來說,這種等待所有工作完成的機制是一種防止泄漏的手段,並不適合你的目的。
有兩種方法可以控制測試主體內的協程執行:
TestDispatcher
上安排的所有任務。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.