JUnit测试中Mock及LiveData返回值为空问题排查与解决
问题根源:初始化顺序错误
你遇到的NullPointerException,核心问题出在ViewModel初始化和Mock设置的顺序搞反了。
看你的setup()方法流程:
- 先初始化了
viewModel = PersonListViewModel(personRepository) - 之后才给
personRepository.getPersonList()设置Mock返回值
而PersonListViewModel的personList属性是在构造函数执行时就直接赋值的:
val personList: LiveData<List<Person>> = personRepository.getPersonList()
这意味着,当你创建ViewModel实例的时候,personRepository.getPersonList()已经被调用了——但此时你还没设置Mock的返回值,Mockito默认会返回null,所以viewModel.personList就变成了null,后续再设置Mock也无法改变这个已经初始化好的属性值。
解决方案:调整初始化顺序
只需要把Mock返回值的设置放在ViewModel实例化之前,就能解决这个问题。具体步骤如下:
- 先创建好要返回的LiveData实例并设置值
- 用
when()指定personRepository.getPersonList()的返回结果 - 最后再初始化
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
相关产品推荐
相关产品推荐

