我目前正在為一個小型 Android 應用撰寫單元測驗,我遇到了一個相當奇怪的錯誤。
我正在使用 Kotlin coroutines 和 Retrofit 2 來向一個 API 發出簡單的 HTTP GET 請求。總的來說,該應用程式正在按預期運行,并且我已經使用 MockWebServer 撰寫了測驗,除了在嘗試測驗來自 API 的錯誤回應時,該測驗也運行良好(在某種程度上相當諷刺)。
基本上,當我故意創建一個錯誤回應時,事情被呼叫的順序是完全不正常的。
以下是相關的測驗代碼:
@Test
fun viewModel_loadData_correctErrorHandling() {
mockServer.enqueue(MockResponse().apply {
setResponseCode(500)
})
viewModel.loadModel()
assert(!viewModel.loading)
assert(viewModel.loadingVisibility.value != View.VISIBLE)
assertNotNull(viewModel.currentError)
assert(viewModel.errorVisibility.value == View.VISIBLE)
assertNotNull(viewModel.model.value)
assert(viewModel.contentVisibility.value != View.VISIBLE)
}
viewModel.loadModel()函式如下:
fun loadModel() {
currentError = null {
loading = true
model.value = null
interactor.load(viewModelScope, @MainThread {
loading = false
model.value = it
}, @MainThread {
loading = false {
currentError = it
Timber.e(it)
})
}
最后,interactor.load函式如下。
fun load(
范圍。CoroutineScope,
onSuccess: (List<ConsumableCategory> ) -> Unit,
one rror: (Throwable) -> Unit, one rror.
) {
范圍.啟動 {
try {
onSuccess(dataManager.getConsumableCategories())
} catch (t: Throwable) {
one rror(t)
}
}
dataManager.getConsumableCategories()只是參考了一個由我的Retrofit實體創建的懸浮函式的呼叫。
當運行相關的測驗時,我的輸出如下:
202109-16T20:05:27. 648 0200 [DEBUG] [TestEventLogger] loadModel start
2021-09-16T20:05:27.648 0200 [DEBUG] [TestEventLogger] Pre Scope
2021-09-16T20:05:27.672 0200 [DEBUG] [TestEventLogger] Post Scope
2021-09-16T20:05:27.672 0200 [DEBUG] [TestEventLogger] loadModel end
2021-09-16T20:05:27.681 0200 [DEBUG] [TestEventLogger] one rror start
2021-09-16T20:05:27.740 0200 [DEBUG] [TestEventLogger]
2021-09-16T20:05:27. 740 0200 [DEBUG] [TestEventLogger] com.kenthawkings.mobiquityassessment.consumableViewModelTest > viewModel_loadData_correctErrorHandling FAILED
2021-09-16T20:05:27.741 0200【DEBUG】【TestEventLogger】java.lang.AssertionError。斷言失敗
2021-09-16T20:05:27.741 0200 [DEBUG] [TestEventLogger] at com. kenthawkings.mobiquityassessment.ConsumableViewModelTest.viewModel_loadData_correctErrorHandling(ConsumableViewModelTest.kt:104)
...
不知為何,我的onError塊在loadModel函式完成后被呼叫。因此,assert(!viewModel.loading)一行失敗了,因為它在loading變數被設定為false的onError回呼之前被呼叫。我正在使用自定義規則來確保一切都在同步運行。
@get:Rule
val testInstantTaskExecutorRule = InstantTaskExecutorRule()
@ExperimentalCoroutinesApi
@get:Rule[/span
val mainCoroutineRule = MainCoroutineRule()
我已經嘗試使用runBlocking和runBlockingTest(兩者都包裹著整個測驗或只是viewModel.loadModel()行),但沒有任何區別。我試著從使用try-catch切換到使用CoroutineExceptionHandler和使用kotlin.runCatching,但我總是得到同樣的結果。
真正奇怪的是,成功回應測驗如期進行,所有的陳述句都 "按順序 "列印。
@Test
fun viewModel_loadData_correctSuccessHandling() {
val reader = MockResponseFileReader("success_response.json" )
assertNotNull(reader.content)
mockServer.enqueue(MockResponse().apply {
setResponseCode(200)
setBody(reader.content)
setHeader("content-type"/span>, "application/json"/span>)
})
viewModel.loadModel()
assert(!viewModel.loading)
assert(viewModel.loadingVisibility.value != View.VISIBLE)
assertNull(viewModel.currentError)
assert(viewModel.errorVisibility.value != View.VISIBLE)
assertNotNull(viewModel.model.value)
assert(viewModel.contentVisibility.value == View.VISIBLE)
}
202109-16T20:05:27. 542 0200 [DEBUG] [TestEventLogger] loadModel start
2021-09-16T20:05:27.550 0200 [DEBUG] [TestEventLogger] Pre Scope
2021-09-16T20:05:27.629 0200 [DEBUG] [TestEventLogger] onSuccess start
2021-09-16T20:05:27.630 0200 [DEBUG] [TestEventLogger] onSuccess end
2021-09-16T20:05:27.630 0200 [DEBUG] [TestEventLogger] Post Scope
2021-09-16T20:05:27.630 0200 [DEBUG] [TestEventLogger] loadModel end
我對Kotlin coroutines相當陌生,但我在Google上做了很多關于這個問題的搜索,似乎沒有人有這個問題,所以我只能假設我在這里做了一些非常愚蠢的事情......
uj5u.com熱心網友回復:我正在使用自定義規則來確保一切都在同步運行。
你所使用的規則改變了AndroidDispatchers.MAIN dispatcher和LiveData相關執行器的執行行為。
Retrofit通過Call.enqueue方法實作了suspend功能,該方法使用了OkHttp提供的執行器,并且不能保證是同步的。
解決這個問題的方法是通過使用scope.launch回傳的Job物件并在你的測驗中呼叫.join(),這可以確保在你嘗試斷言其行為之前完成coroutine。
真正奇怪的部分 當運行我的成功回應測驗時,一切都按預期進行,所有的陳述句都 "按順序 "列印。
這實際上是由Retrofit的一個實施怪癖引起的。該庫的作者實際上已經寫了一篇關于它的博文。Jake Wharton - Exceptions and proxies and coroutines, oh my!。
。基本上,Retrofit 的suspend支持不能同步回傳,如果它必須拋出一個例外,它總是必須通過調度器。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/328079.html
標籤:
上一篇:【歷史上的今天】10 月 20 日:微軟黑屏事件;Ubuntu Linux 作業系統發布;Apple Pay 正式上線
下一篇:遷移學習神經網路不是學習
