This repository demonstrates a zero-boilerplate way to generate screenshot tests for all your Jetpack Compose @Previews using JVM unit tests. It’s built on top of Now in Android and uses Paparazzi under the hood (no emulator required). It provider an easy to replicate pattern for broad UI coverage with minimal code.
PreviewTests.kt— Discovers previews at runtime and runs Paparazzi snapshots for each.core/ui/screenshottest— Tiny, focused utilities for test behaviour configuration:IgnoreScreenshotTest.kt— Opt-out any preview from the sweep.ScreenshotTestParameters.kt— Per-preview render configuration (e.g., NORMAL vs SHRINK).NamedPreviewParameterProvider.kt— Preserves meaningful names forPreviewParameterProviderinputs.
-
Discovery via reflection: all functions annotated with
@Previeware found at test time. Multi-preview annotations used in Now in Android (@DevicePreviews,@ThemePreviews) are treated as regular previews and included automatically. -
Per-preview configuration: optional
@ScreenshotTestParameterslets a preview adjust its Paparazzi rendering mode (defaults toSHRINK, can switch toNORMAL) without touching the test harness. This can easily be expanded to pass any additional configuration to the test that is required for a project. -
Parameterized execution: a single JUnit test iterates all discovered previews and calls
paparazzi.snapshot { ... }for each one. -
Opt-out: add
@IgnoreScreenshotTeston a preview to exclude it from the sweep. -
No emulator / instrumentation: everything runs as a JVM unit test using Paparazzi, so it’s fast and stable.
Record snapshots (first run or when intentionally updating baselines): ./gradlew clean screenshot-test:recordPaparazziProdRelease
Verify snapshots (regular PR use): ./gradlew clean screenshot-test:verifyPaparazziProdRelease
- Pass: no diffs vs recorded images.
- Fail: visual diffs detected; re-record when changes are expected.
-
What to copy
- The test driver pattern from
screenshot-test/.../PreviewTests.kt. - The helper annotations and utilities (if needed) from
core/ui/.../screenshottest:IgnoreScreenshotTest.ktScreenshotTestParameters.ktNamedPreviewParameterProvider.kt
- The test driver pattern from
-
What to set up (Kotlin DSL)
- Apply the Paparazzi Gradle plugin in your test module.
- Add dependencies used by the approach: Paparazzi (JUnit rule),
org.jetbrains.kotlin:kotlin-reflect,org.reflections:reflections(or ClassGraph), andcom.google.testparameterinjector:test-parameter-injector. - Point the reflection scan to your app’s base package.
-
Project tweaks
- Previews can be
publicorprivate. By default, both are handled and private previews are made accessible during discovery. - Adjust or remove multi-preview annotation detection. The example uses
ThemePreviewsandDevicePreviewsfrom the Now in Android project.
- Previews can be
- Coverage without ceremony: turns the previews you already maintain into screenshot tests automatically.
- Deterministic & fast due to Paparazzi: runs as JVM tests, no emulator flakiness.
- Low maintenance: a tiny test harness + a couple of lightweight annotations enable complete preview test coverage.
- Scales with teams: new UI = new preview = new snapshot, with sensible naming and baselines.
- Clear trade-offs: reflection adds a small discovery cost, but avoids per-screen boilerplate and keeps the codebase clean. Another downside is build times for test execution: even though Paparazzi does not require an emulater and tests are very fast, for very large codebases it might be sensible to split preview detection and test execution on a per-module basis for incremental builds & testing.
This project is licensed under Apache 2.0. See LICENSE for details.