Files

2.5 KiB

Automated testing for camera features is notoriously difficult because you can't easily mock physical hardware, lighting, or motion. This guide provides patterns for reliable, hermetic camera tests.

Develop a testing mindset

Challenge Conventional approach Camera technical approach
Frameworks Mockito or MockK Fakes over mocks
Assertions assertEquals, assertTrue Google Truth, assertThat
Environment Implied environments Explicit @RunWith annotations
Async operations Thread.sleep Explicit timeoutMillis or IdlingResource

Follow the implementation guide

Fakes over mocks

Don't use Mockito. Relying on mocks for complex, rapidly changing interfaces like ImageProxy or CameraInfo makes tests brittle. Instead, build "Fake" implementations that verify state rather than behavior.

// Create a Fake ImageProxy for ML Testing (Fakes over Mocks)
val fakeImage = FakeImageProxy(w = 640, h = 480)

// Feed the fake buffer into your analyzer

Mock camera capabilities

Use FakeAppConfig from androidx.camera:camera-testing to simulate specific hardware constraints in tests, such as a device without a flash.

// Use awaitInstance() extension function for coroutine-based provider retrieval
val cameraProvider = ProcessCameraProvider.awaitInstance(context)

Use Truth assertions

Use Google Truth, assertThat, instead of standard JUnit assertions. It provides more readable assertion chains and useful failure messages.

Test asynchronous lifecycles

Camera initialization is asynchronous. Use IdlingResource to ensure your test waits for the UseCase to be bound before asserting.

To test asynchronous lifecycles, use the following pattern:

  1. Wrap the ProcessCameraProvider initialization in a CountDownLatch or IdlingResource.
  2. Assert only after the cameraControl instance is non-null.

Testing pitfalls

  • Resource leaks : To prevent "Camera in Use" errors, in your @After block, call cameraProvider.unbindAll.
  • The "flaky initializer": Camera tests often fail on CI because the "Virtual Camera" takes too long to warm up. Use a sufficient explicit timeout for the first initialization.
  • Permission blockers : To bypass the system permission dialogs, in your Espresso tests, use GrantPermissionRule.
  • Resolution mismatch : Tests on emulators often default to 640x480. Ensure your ResolutionSelector handles this low-res fallback correctly.