You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mockito无法Mock isOnline方法,测试触发真实网络校验报错求助

问题原因

你用Mockito mock isOnline 方法无效,核心原因是该方法是目标类(sut)的非open成员方法。Mockito默认基于动态代理实现mock,只能处理接口或标记为open的类/方法;普通final方法(Kotlin默认方法为final)会绕过mock逻辑,直接执行真实的网络校验代码,导致测试报错。

解决方案

方案一:重构代码,抽离网络校验依赖(推荐)

将网络判断逻辑从ViewModel中剥离为独立工具类,通过依赖注入引入,这样可以完全隔离真实网络逻辑,测试时直接mock这个工具类即可。

1. 新建网络工具类

class NetworkConnectivityHelper(private val context: Context) {
    fun isOnline(): Boolean {
        val connectivityManager =
            context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

        val capabilities =
            connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
        if (capabilities != null) {
            if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
                return true
            } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
                return true
            }
        }
        return false
    }
}

2. 修改ViewModel,注入工具类

class YourViewModel(
    private val networkHelper: NetworkConnectivityHelper,
    private val getCharactersFromDataBaseUseCase: GetCharactersFromDataBaseUseCase,
    private val getAllCharactersUseCase: GetAllCharactersUseCase
) : ViewModel() {
    private val _getAllCharactersResponseLiveData = MutableLiveData<Resource<List<Character>>>()
    val getAllCharactersLiveData: LiveData<Resource<List<Character>>> = _getAllCharactersResponseLiveData

    fun getAllCharacters() = viewModelScope.launch {
        _getAllCharactersResponseLiveData.postValue(Resource.Loading())
        val charactersList = getCharactersFromDataBaseUseCase.getAllCharactersFromDataBase()

        if (networkHelper.isOnline() && charactersList.isEmpty()) {
            _getAllCharactersResponseLiveData.postValue(getAllCharactersUseCase.getAllCharacters())
        } else if (!networkHelper.isOnline() && charactersList.isNotEmpty() ||
            networkHelper.isOnline() && charactersList.isNotEmpty()
        ) {
            _getAllCharactersResponseLiveData.postValue(
                Resource.Success(
                    getCharactersFromDataBaseUseCase.getAllCharactersFromDataBase()
                )
            )
        } else {
            _getAllCharactersResponseLiveData.postValue(Resource.Error())
        }
    }
}

3. 测试代码中mock工具类

@Test
fun internet_connected_and_empty_data_base_gets_info_from_api_success() {
    // given
    val mockNetworkHelper = mock(NetworkConnectivityHelper::class.java)
    `when`(mockNetworkHelper.isOnline()).thenReturn(true)
    // mock其他依赖的返回值
    `when`(getCharactersFromDataBaseUseCase.getAllCharactersFromDataBase()).thenReturn(emptyList())
    `when`(getAllCharactersUseCase.getAllCharacters()).thenReturn(Resource.Success(listOf(Character(name = "char1"))))
    
    val sut = YourViewModel(mockNetworkHelper, getCharactersFromDataBaseUseCase, getAllCharactersUseCase)

    // when
    sut.getAllCharacters()
    val expected = sut.getAllCharactersLiveData.getOrAwaitValue().data

    // then
    assertThat(expected?.first()?.name).isEqualTo("char1")
}

方案二:用Mockito Spy做部分mock(临时 workaround)

如果不想重构代码,可以用Spy创建目标类的部分mock实例,但需要先把isOnline方法改为open(因为Kotlin默认方法是final,Spy无法代理)。

1. 修改ViewModel的isOnline方法为open

open fun isOnline(context: Context): Boolean {
    // 原逻辑保持不变
}

2. 测试代码使用Spy

@Test
fun internet_connected_and_empty_data_base_gets_info_from_api_success() {
    // given
    val sut = spy(YourViewModel(...)) // 传入构造所需的其他依赖
    `when`(sut.isOnline(context)).thenReturn(true)
    // mock其他依赖的返回值
    `when`(getCharactersFromDataBaseUseCase.getAllCharactersFromDataBase()).thenReturn(emptyList())
    `when`(getAllCharactersUseCase.getAllCharacters()).thenReturn(Resource.Success(listOf(Character(name = "char1"))))

    // when
    sut.getAllCharacters()
    val expected = sut.getAllCharactersLiveData.getOrAwaitValue().data

    // then
    assertThat(expected?.first()?.name).isEqualTo("char1")
}
方案对比
  • 方案一符合SOLID原则,降低ViewModel职责,后续测试、维护更灵活,是长期最优解。
  • 方案二属于临时修复,需要修改方法的访问修饰符,破坏封装性,仅适合快速验证场景。

内容的提问来源于stack exchange,提问作者Tony

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 07:44:16