使用Kotlin协程的Retrofit请求单元测试问题排查
解决Kotlin协程+Retrofit单元测试中Listener未被调用的问题
从你的代码和报错信息来看,测试失败的核心原因是协程在测试环境中没有被正确执行或等待,导致listener的回调还没触发,测试就已经结束了。下面是具体的问题分析和修复步骤:
1. 替换Main Dispatcher为测试专用Dispatcher
你的Interactor中使用了launch(UI)(也就是现在的Dispatchers.Main),但在单元测试环境中,Main dispatcher没有默认的执行线程,协程会被挂起而不执行。你需要在测试中替换它为Test Dispatcher:
首先确保添加了kotlinx-coroutines-test依赖,然后在测试类中配置:
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.test.TestDispatcher import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.setMain import org.junit.After import org.junit.Before class WorklistInteractorTest { private val testDispatcher: TestDispatcher = UnconfinedTestDispatcher() @Before fun setup() { Dispatchers.setMain(testDispatcher) } @After fun teardown() { Dispatchers.resetMain() } // 你的测试方法... }
2. 使用runTest而非runBlocking执行测试
runBlocking不会自动等待所有协程完成,而runTest(来自kotlinx-coroutines-test)是专门为协程测试设计的,它会确保所有调度的协程都执行完毕:
@Test fun `when worklistquery returns result, pass result back through listeners onWorklistResult`() = runTest { whenever(mWorklistRepositoryMock.getWorklist(anyString(), anyInt())) .thenReturn(Calls.response(expectedWorklistResult)) mInteractor.getWorklist(mOnWorklistResultListenerMock) verify(mOnWorklistResultListenerMock).onWorklistResult(expectedWorklistResult) verifyNoMoreInteractions(mOnWorklistResultListenerMock) }
3. 移除不必要的async包裹
你的Interactor代码中,launch里面再套一层async是多余的,这会增加协程层级,也可能导致测试中等待逻辑混乱。可以简化代码:
override fun getWorklist(listener: OnWorklistResultListener) { job = launch(Dispatchers.Main) { // 建议用Dispatchers.Main而非过时的UI val result = worklistRepository.getWorklist( ip = preferenceManager.worklistIp, port = preferenceManager.worklistPort ).awaitResult() when (result) { is Result.Ok -> listener.onWorklistResult(result.value) is Result.Error -> { Timber.e(result.exception, "HTTP error with code %s}", result.exception.code()) when(result.exception.code()) { 401 -> listener.onInvalidCredentialsFailure() 500 -> listener.internalServerError() 503 -> listener.noServerResponseFailure() else -> listener.onError(result.exception.cause.toString()) } } is Result.Exception -> { Timber.e(result.exception.cause, "Exception with cause %s", result.exception.cause.toString()) when(result.exception) { is ConnectException -> listener.connectionRefused() is SocketTimeoutException -> listener.failedToConnectToHost() else -> listener.onError(result.exception.cause.toString()) } } } } }
4. 确保Retrofit Call的Mock正确
确认你的mWorklistRepositoryMock.getWorklist返回的Call在调用awaitResult()时会生成Result.Ok。如果用的是Retrofit的Calls.response,需要确保它是成功的响应,能正确解析成你的expectedWorklistResult对象。
内容的提问来源于stack exchange,提问作者Bohsen
相关产品推荐
相关产品推荐

