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

JUnit测试中Mock及LiveData返回值为空问题排查与解决

问题根源:初始化顺序错误

你遇到的NullPointerException,核心问题出在ViewModel初始化和Mock设置的顺序搞反了。

看你的setup()方法流程:

  1. 先初始化了viewModel = PersonListViewModel(personRepository)
  2. 之后才给personRepository.getPersonList()设置Mock返回值

而PersonListViewModel的personList属性是在构造函数执行时就直接赋值的:

val personList: LiveData<List<Person>> = personRepository.getPersonList()

这意味着,当你创建ViewModel实例的时候,personRepository.getPersonList()已经被调用了——但此时你还没设置Mock的返回值,Mockito默认会返回null,所以viewModel.personList就变成了null,后续再设置Mock也无法改变这个已经初始化好的属性值。


解决方案:调整初始化顺序

只需要把Mock返回值的设置放在ViewModel实例化之前,就能解决这个问题。具体步骤如下:

  1. 先创建好要返回的LiveData实例并设置值
  2. 用when()指定personRepository.getPersonList()的返回结果
  3. 最后再初始化PersonListViewModel

修正后的setup()方法代码如下:

@Before
fun setup() {
    MockitoAnnotations.initMocks(this)
    
    // 1. 准备测试数据和LiveData
    val personList = listOf(Person("id1"), Person("id2"))
    val personListLiveData = MutableLiveData<List<Person>>()
    personListLiveData.postValue(personList)
    
    // 2. 先设置Mock的返回值
    `when`(personRepository.getPersonList()).thenReturn(personListLiveData)
    
    // 3. 最后再初始化ViewModel
    viewModel = PersonListViewModel(personRepository)
    
    // 现在personList已经有值了,不会抛出NPE
    viewModel.personList.observeForever(personListObserver)
}

额外的测试优化建议
  • 如果你用的是Mockito 2+,可以考虑改用@ExtendWith(MockitoExtension::class)替代MockitoAnnotations.initMocks(this),让测试代码更简洁。
  • 对于LiveData的测试,除了InstantTaskExecutorRule,还可以使用observeForTesting扩展函数(来自androidx.arch.core:core-testing依赖),不需要手动调用observeForever和后续的removeObserver,避免内存泄漏。比如:
@Test
fun testPersonListPopulates() {
    viewModel.personList.observeForTesting {
        assertEquals(2, viewModel.personList.value?.size ?: 0)
    }
}

这样测试代码更健壮,也不用手动管理Observer的生命周期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:05:50