Kotlin单元测试中如何Mock Handler(Looper)?代码测试咨询
在Kotlin单元测试中用Mockito处理Handler延迟执行代码
直接测试这段包含postDelayed的代码会遇到两个问题:单元测试环境默认没有主线程Looper,导致代码无法正常执行;2秒延迟会拖慢测试速度,还可能导致断言时机错误。用Mockito可以通过以下方式解决:
步骤1:改造原代码,抽离Handler依赖
为了能在测试中替换Handler,需要把Handler的创建逻辑抽成可覆盖的方法:
// 原业务类 class YourBusinessClass { lateinit var managerService: ManagerService var fwStatus: String = "--" private val versionValue = "Firmware: " // 假设这是类中已有的变量 // 抽离Handler创建方法,测试时可覆盖 open fun getMainHandler(): Handler = Handler(Looper.getMainLooper()) // 触发延迟逻辑的方法(假设原代码是在这个方法里) fun startFirmwareCheck() { getMainHandler().postDelayed( { println("inside the looper") val firmwareVersion = managerService.getDeviceFirmwareVersion() fwStatus = if (!firmwareVersion.isNullOrEmpty()) "$versionValue $firmwareVersion" else "--" UpdateManagerInstance.disconnectFromUpdateService() }, 2000 ) } }
步骤2:编写Mockito测试用例
通过mock Handler捕获延迟任务,直接执行跳过延迟,同时mock依赖的服务验证逻辑:
import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock import org.mockito.junit.MockitoJUnitRunner import org.mockito.kotlin.* @RunWith(MockitoJUnitRunner::class) class YourBusinessClassTest { @Mock lateinit var mockHandler: Handler @Mock lateinit var mockManagerService: ManagerService @Captor lateinit var runnableCaptor: ArgumentCaptor<Runnable> private lateinit var businessClass: YourBusinessClass @Test fun `test firmware check logic after delayed task`() { // 初始化测试类,替换为mock的Handler businessClass = object : YourBusinessClass() { override fun getMainHandler(): Handler = mockHandler } businessClass.managerService = mockManagerService // 模拟managerService返回固件版本 whenever(mockManagerService.getDeviceFirmwareVersion()).thenReturn("v2.1.0") // 触发延迟逻辑 businessClass.startFirmwareCheck() // 验证Handler的postDelayed被调用,捕获传入的Runnable verify(mockHandler).postDelayed(runnableCaptor.capture(), eq(2000L)) // 直接执行捕获到的Runnable,跳过2秒延迟 runnableCaptor.value.run() // 断言固件状态是否正确设置 assert(businessClass.fwStatus == "Firmware: v2.1.0") // 验证单例服务的断开方法被调用 mockStatic(UpdateManagerInstance::class.java) { verify(UpdateManagerInstance).disconnectFromUpdateService() } } }
关键说明
- 抽离Handler:通过开放Handler的创建方法,让测试可以注入mock对象,避免依赖真实的主线程Looper。
- 捕获Runnable:用
ArgumentCaptor捕获延迟任务,直接执行就能同步完成逻辑验证,不需要等待真实延迟。 - Mock依赖:对
managerService和单例UpdateManagerInstance进行mock,确保测试只关注当前逻辑,不受外部依赖影响。
内容的提问来源于stack exchange,提问作者Xuai
相关产品推荐
相关产品推荐

