Android开发:如何Mock HandlerThread以编写测试用例?
如何Mock构造方法中创建的HandlerThread
推荐方案:重构代码实现依赖注入
直接在构造方法里硬编码创建HandlerThread会导致测试难以Mock,最稳妥的方式是把HandlerThread(或更底层的Looper/WorkHandler)作为依赖传入类中,同时保留无参构造供业务代码使用:
原有代码:
class YourClass { private lateinit var workHandler: WorkHandler init { HandlerThread("TimeoutHandlerThread").apply { start() workHandler = WorkHandler(looper) } } }
重构后:
class YourClass(private val handlerThread: HandlerThread) { private lateinit var workHandler: WorkHandler init { handlerThread.start() workHandler = WorkHandler(handlerThread.looper) } // 保留无参构造,供业务代码调用 constructor() : this(HandlerThread("TimeoutHandlerThread")) }
测试时直接MockHandlerThread传入即可:
import org.mockito.ArgumentCaptor import org.mockito.Mockito.* import org.junit.Test class YourClassTest { @Test fun testWorkHandlerInitialization() { // Mock HandlerThread 和 Looper val mockHandlerThread = mock(HandlerThread::class.java) val mockLooper = mock(Looper::class.java) `when`(mockHandlerThread.looper).thenReturn(mockLooper) // 创建被测类,传入Mock实例 val yourClass = YourClass(mockHandlerThread) // 验证HandlerThread的start方法被调用 verify(mockHandlerThread).start() // 验证WorkHandler使用了Mock的Looper初始化 val looperCaptor = ArgumentCaptor.forClass(Looper::class.java) verify(WorkHandler::class.java).construct(looperCaptor.capture()) assert(looperCaptor.value == mockLooper) } }
不重构的方案:用Mockito静态Mock拦截构造调用
如果不想修改原有代码,可以用Mockito 3.4.0及以上版本的mockStatic功能,拦截HandlerThread的构造调用,返回Mock实例:
import org.mockito.ArgumentCaptor import org.mockito.Mockito.* import org.junit.Test class YourClassTest { @Test fun testWithoutRefactoring() { mockStatic(HandlerThread::class.java).use { mockedStatic -> // 创建Mock的HandlerThread和Looper val mockHandlerThread = mock(HandlerThread::class.java) val mockLooper = mock(Looper::class.java) `when`(mockHandlerThread.looper).thenReturn(mockLooper) // 拦截构造方法,返回Mock实例 mockedStatic.`when` { HandlerThread("TimeoutHandlerThread") } .thenReturn(mockHandlerThread) // 创建被测类 val yourClass = YourClass() // 验证构造方法被调用 mockedStatic.verify { HandlerThread("TimeoutHandlerThread") } // 验证start方法被调用 verify(mockHandlerThread).start() // 验证WorkHandler使用了Mock的Looper val looperCaptor = ArgumentCaptor.forClass(Looper::class.java) verify(WorkHandler::class.java).construct(looperCaptor.capture()) assert(looperCaptor.value == mockLooper) } } }
注意事项
- 如果
WorkHandler是私有内部类,需要调整访问权限,或者直接MockWorkHandler本身来验证逻辑。 - 测试中要避免启动真实线程,所有和线程、Looper相关的逻辑都用Mock替代,防止测试卡住或产生异步问题。
内容的提问来源于stack exchange,提问作者Abu Yousuf
相关产品推荐
相关产品推荐

