Android入门开发者如何为CameraX的startCamera方法编写单元测试
原有测试代码问题点
- 直接mock了整个CameraFragment,
startCamera方法会走空实现,无法触发真实业务逻辑 - 未对
ProcessCameraProvider.getInstance静态方法做Mock,会触发真实的CameraX初始化逻辑,本地JVM环境无法正常执行 - 未处理异步回调逻辑,Listener内的代码不会自动执行,无法验证后续逻辑
startCamera无返回值,无法通过返回值做断言
前置依赖配置
在模块级build.gradle的dependencies中添加以下测试依赖:
testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:4.8.1' testImplementation 'org.mockito:mockito-inline:4.8.1' // 支持静态方法Mock testImplementation 'androidx.test:core:1.5.0' testImplementation 'org.robolectric:robolectric:4.9' // 本地JVM模拟Android环境 testImplementation 'androidx.test.ext:junit:1.1.5'
可运行测试代码示例
@RunWith(RobolectricTestRunner::class) class CameraFragmentTest { // 模拟的CameraProvider实例 private val mockCameraProvider = mock(ProcessCameraProvider::class.java) // 模拟的ListenableFuture实例 private val mockFuture = mock(ListenableFuture::class.java) as ListenableFuture<ProcessCameraProvider> // 测试用的CameraFragment实例,用spy保留真实方法逻辑 private lateinit var cameraFragment: CameraFragment @Before fun setUp() { // 初始化Fragment实例 cameraFragment = spy(CameraFragment()) // 模拟返回测试Context doReturn(ApplicationProvider.getApplicationContext<Context>()) .`when`(cameraFragment).requireContext() // 模拟setCameraConfig方法,避免执行真实的相机配置逻辑 doNothing().`when`(cameraFragment).setCameraConfig(any(), any()) // 模拟Future.get返回我们mock的CameraProvider doReturn(mockCameraProvider).`when`(mockFuture).get() } @Test fun `startCamera 正常流程验证`() { // 1. Mock ProcessCameraProvider的静态getInstance方法 Mockito.mockStatic(ProcessCameraProvider::class.java).use { mockedStatic -> mockedStatic.`when`<ListenableFuture<ProcessCameraProvider>> { ProcessCameraProvider.getInstance(any()) }.thenReturn(mockFuture) // 2. 调用待测试的startCamera方法 cameraFragment.startCamera() // 3. 捕获Future.addListener传入的Runnable,主动执行触发回调逻辑 val captor = ArgumentCaptor.forClass(Runnable::class.java) verify(mockFuture).addListener(captor.capture(), any()) captor.value.run() // 4. 验证逻辑执行是否符合预期 // 验证setCameraConfig被调用,且传入的cameraProvider是我们mock的实例 verify(cameraFragment).setCameraConfig(eq(mockCameraProvider), any(CameraSelector::class.java)) // 可选:验证CameraSelector的镜头朝向是否符合配置的cameraSelectorOption val selectorCaptor = ArgumentCaptor.forClass(CameraSelector::class.java) verify(cameraFragment).setCameraConfig(any(), selectorCaptor.capture()) assertEquals(cameraFragment.cameraSelectorOption, selectorCaptor.value.lensFacing) } } }
核心逻辑说明
- 采用Robolectric模拟Android运行环境,不需要真机/模拟器即可在本地JVM执行测试,执行效率更高
- 使用spy创建CameraFragment实例,既可以保留
startCamera的真实业务逻辑,又可以MockrequireContext、setCameraConfig等依赖方法 - 通过Mock静态方法拦截
ProcessCameraProvider.getInstance调用,返回模拟的Future实例,避免依赖真实相机硬件 - 主动触发Listener的run方法,跳过异步等待逻辑,直接验证回调内的业务执行结果
入门单元测试学习资料推荐
- 《Android单元测试实战》:内容贴合Android业务场景,从基础框架用法到复杂场景测试都有清晰讲解,适合入门建立完整知识体系
- Android官方开发者平台单元测试指南:内容权威,清晰划分本地单元测试、Instrumented测试的适用场景,提供标准最佳实践参考
- 国内视频平台Android单元测试入门实操系列:配套代码实操演示,可跟着步骤从零搭建测试框架,降低入门门槛
内容的提问来源于stack exchange,提问作者Julia
相关产品推荐
相关产品推荐

