无法Mock Retrofit调用:单元测试未触发onResponse问题
Retrofit Mock单元测试失效问题修复
你的单元测试只打印「end」却未触发onResponse(),核心问题及修复方案如下:
1. 测试未触发ViewModel核心方法
你的测试代码仅完成了SensorsService和Call的Mock配置,但从未调用ViewModel的loadSensors()方法——整个API调用逻辑根本没执行,自然不会走到onResponse()。
2. 接口定义与实际调用不匹配
你提供的接口是无参方法:
@GET("/sensors") fun getUserSensors(): Call<List<Long>>
但ViewModel里调用的是带两个参数的版本:
sensorsService.getUserSensors(getUserToken(), getUserId())
这会导致你Mock的every { sensorsService.getUserSensors(any(), any()) }无法匹配实际调用(接口本身没有这个重载)。先统一接口和调用逻辑,比如修正接口为:
@GET("/sensors") fun getUserSensors( @Header("Authorization") token: String, @Query("userId") userId: String ): Call<List<Long>>
3. 协程未等待执行完成
ViewModel用CoroutineScope(Dispatchers.IO).launch启动协程,测试函数执行完会直接结束,协程里的代码可能还没跑完。用官方推荐的runTest(需依赖kotlinx-coroutines-test)可以自动处理协程调度,确保代码执行完成。
修复后的完整测试代码
@OptIn(ExperimentalCoroutinesApi::class) class YourViewModelTest { private lateinit var viewModel: YourViewModel private val sensorsService: SensorsService = mockk() @Before fun setup() { // 注入Mock的service,初始化ViewModel viewModel = YourViewModel(sensorsService) } @Test fun loadSensors_triggersOnResponse() = runTest { val mockedCall = mockk<retrofit2.Call<List<Long>>>() // Mock enqueue逻辑,直接触发onResponse every { mockedCall.enqueue(any()) } answers { val callback = args[0] as retrofit2.Callback<List<Long>> val response = retrofit2.Response.success(listOf(1L, 2L, 3L)) callback.onResponse(mockedCall, response) } // 匹配接口调用参数(根据实际接口定义调整) every { sensorsService.getUserSensors(any(), any()) } returns mockedCall // 触发ViewModel的核心方法 viewModel.loadSensors() // 等待所有协程任务执行完成 advanceUntilIdle() // 这里可添加断言验证逻辑,比如检查LiveData是否更新 } }
额外优化建议
- ViewModel里别直接用
CoroutineScope(Dispatchers.IO),改用viewModelScope(属于androidx.lifecycle库),它会在ViewModel销毁时自动取消协程,更安全可靠。 - 测试优先用
runTest替代手动设置线程,简化协程调度控制。
内容的提问来源于stack exchange,提问作者Captain Jacky
相关产品推荐
相关产品推荐

