如何为观察其他类LiveData的Android Kotlin函数编写单元测试
LiveData观察逻辑单元测试实战
问题背景
我们需要为绑定Activity生命周期的LiveData观察方法编写单元测试,校验指定状态触发时的导航分支逻辑正确性。
待测业务代码
override fun observe(activity: AppCompatActivity) { appCoordinatorManager.getState().observe(activity) { when(it) { is AppCoordinatorManager.State.AssociateWithReferralProgramSuccess -> { navigator.navigateToDashboard(null) } } } }
关联依赖代码
AppCoordinatorManager通过单事件LiveData对外推送状态:
private val state = LiveEvent<State>() fun getState(): LiveData<State> = state sealed class State { object AssociateWithReferralProgramSuccess: State() }
初始编写的测试代码(执行时会校验失败)
@Test fun `Receiving AssociateWithReferralProgramSuccess when observing state from AppCoordinatorManager`() { whenever(appCoordinatorManager.getState()).thenReturn(LiveEvent<AppCoordinatorManager.State>()) appCoordinator.observe(activity) appCoordinatorManager.setPrivateField("state", LiveEvent<AppCoordinatorManager.State.AssociateWithReferralProgramSuccess>()) verify(navigator).navigateToDashboard(null) }
测试运行失败的核心原因
- 打桩对象与实际发射事件的对象完全不匹配:测试先对
getState()打桩返回了一个独立的空LiveEvent实例,后续通过反射修改AppCoordinatorManager内部私有字段时,又创建了另一个带状态的LiveEvent实例。观察者注册在之前的空实例上,根本收不到后续新实例发射的状态。 - 模拟的Activity未达到活跃状态:LiveData绑定LifecycleOwner时,只有当生命周期状态至少达到
STARTED级别,观察者才会收到状态回调。测试中的模拟activity默认是未激活状态,即便状态发射正确,回调也不会触发。 - 缺少LiveData测试调度规则:没有配置
InstantTaskExecutorRule时,LiveData默认将回调投递到主线程队列,单元测试环境下没有可运行的主线程消息循环,回调会被阻塞无法即时执行,导致verify执行时回调逻辑还未运行。
可正常运行的测试实现
需要在测试类中补充必要的测试规则,统一使用同一个可手动控制的LiveData实例完成打桩和状态发射:
- 添加
InstantTaskExecutorRule,强制LiveData的回调同步执行,不受线程调度影响 - 构造处于活跃状态的Lifecycle,绑定到模拟的Activity上,保证观察者可正常接收回调
- 统一使用同一个LiveEvent实例完成打桩和状态发射,避免出现对象错位
参考实现代码:
@get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule() @Test fun `Receiving AssociateWithReferralProgramSuccess when observing state from AppCoordinatorManager`() { // 构造统一使用的测试LiveEvent实例 val testLiveEvent = LiveEvent<AppCoordinatorManager.State>() // 打桩直接返回该实例,后续所有观察逻辑都绑定到这个实例上 whenever(appCoordinatorManager.getState()).thenReturn(testLiveEvent) // 构造处于活跃状态的Lifecycle val testLifecycle = LifecycleRegistry(mock(LifecycleOwner::class.java)) testLifecycle.currentState = Lifecycle.State.RESUMED whenever(activity.lifecycle).thenReturn(testLifecycle) // 注册观察者 appCoordinator.observe(activity) // 发射目标状态 testLiveEvent.value = AppCoordinatorManager.State.AssociateWithReferralProgramSuccess // 校验导航逻辑触发 verify(navigator).navigateToDashboard(null) }
内容的提问来源于stack exchange,提问作者Edhar Khimich
相关产品推荐
相关产品推荐

