Kotlin单元测试遇Mockito无法mock final类DataRepository问题求助
问题排查与解决方案
错误原因
- 原因1:Kotlin类默认被final修饰,Mockito核心库默认不支持mock/spy final类,直接触发你遇到的报错。
- 原因2:测试代码中错误将
MainActivityViewModel标记为@Mock,你需要测试ViewModel的真实逻辑,mock后的ViewModel不会执行真实的getCreditReport方法,自然无法触发Repository调用。 - 原因3(潜在问题):ViewModel中使用了
Dispatchers.IO调度器,单元测试环境没有Android主线程调度器,不做处理会触发运行时异常。
修复步骤
步骤1:解决final类无法mock的问题,三选一即可
- 方案1(最便捷):引入mockito-inline依赖,该组件支持mock final类,在Module级build.gradle的test依赖中添加:
testImplementation 'org.mockito:mockito-inline:4.11.0'
- 方案2:修改
DataRepository类为open类型,解除final限制:
open class DataRepository @Inject constructor( private val retroServiceInterface: RetroServiceInterface ) { // 方法如果需要单独mock也可以加open修饰 open suspend fun getCreditReport(): CreditReport { return retroServiceInterface.getDataFromApi() } }
- 方案3(架构更规范):抽象Repository接口,测试时mock接口(接口默认非final):
interface DataRepositoryInterface { suspend fun getCreditReport(): CreditReport } class DataRepository @Inject constructor( private val retroServiceInterface: RetroServiceInterface ) : DataRepositoryInterface { override suspend fun getCreditReport(): CreditReport { return retroServiceInterface.getDataFromApi() } } // ViewModel构造参数改为接口类型 class MainActivityViewModel @Inject constructor( private val dataRepository: DataRepositoryInterface ) : ViewModel()
步骤2:修正单元测试代码
首先引入协程测试依赖,处理ViewModel中的协程调度问题:
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3"
修正后的测试代码:
@RunWith(MockitoJUnitRunner::class) class MainActivityViewModelTest { @Mock private lateinit var dataRepository: DataRepository private lateinit var mainActivityViewModel: MainActivityViewModel @Before fun setup() { // 替换主线程调度器为测试调度器 Dispatchers.setMain(StandardTestDispatcher()) // 手动实例化真实ViewModel,传入mock的Repository mainActivityViewModel = MainActivityViewModel(dataRepository) } @After fun tearDown() { // 测试完成重置调度器 Dispatchers.resetMain() } @Test fun `getCreditReport调用时触发Repository对应方法`() = runTest { // 调用待测方法 mainActivityViewModel.getCreditReport() // 等待所有协程任务执行完成 advanceUntilIdle() // 验证Repository方法被调用 verify(dataRepository).getCreditReport() } }
内容的提问来源于stack exchange,提问作者BRDroid
相关产品推荐
相关产品推荐

