You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 15:45:10