Experimental Compose Preview Support
Roborazzi provides support for generating screenshot tests and easy setup for Jetpack Compose Preview. This support uses ComposablePreviewScanner to scan the Composable Previews in your project.
Generate Compose Preview screenshot tests
You first need to add the Roborazzi plugin to your project. Please refer to the setup guide for more information. Then you can enable the Compose Preview screenshot test generation feature by adding the following configuration to your build.gradle.kts file:
The plugin will not automatically change your settings or add dependencies to prevent conflicts with your existing setup. However, it will provide instructions on what to do next, such as adding dependencies and required code. You can also check the sample project for a complete example.
After that, you can run the recordRoborazziDebug task to generate screenshots using the generated tests, as described in the setup guide.
Customizing the Preview screenshot test
You can customize the generated test by adding the following configuration to your build.gradle.kts file:
Advanced: Custom ComposePreviewTester Implementation
You can create a custom ComposePreviewTester to control the screenshot capture behavior, such as setting a custom image comparison threshold.
Note that AndroidComposePreviewTester is a final class, so you can't subclass it. Instead, use Kotlin class delegation and pass a custom Capturer to its constructor. Also, your tester class must have a parameterless constructor because the plugin instantiates it via reflection:
If you need to customize more than the capture behavior, such as the scan options or the test lifecycle, you can override options() or test() in the delegating class.
Then reference your custom tester in the Gradle configuration:
Filtering previews by annotation
annotationFilter controls which previews are captured (requires the roborazzi-annotations dependency). By default it is AnnotationFilter.Filter.RoboPreviewExclude, so previews annotated with @RoboPreviewExclude are skipped. Set it to RoboPreviewInclude to capture only previews annotated with @RoboPreviewInclude:
To filter by your own annotations, pass their fully qualified class names (use the JVM binary name with $ for nested classes, e.g. com.example.Outer$Inner):
Compose Multiplatform previews
The Compose Preview support also works with Compose Multiplatform common previews (@Preview in commonMain). You can scan them with the CommonComposablePreviewScanner from the ComposablePreviewScanner common artifact in a custom tester; the generated tests run as Android unit tests with Robolectric. See the multiplatform sample project for a complete setup.
Experimental Compose Desktop Preview Support
Roborazzi can also generate preview screenshot tests for the Compose Desktop (JVM) target, without Robolectric. Previews are scanned with ComposablePreviewScanner's android artifact, which is a pure-JVM jar: it finds the multiplatform androidx.compose.ui.tooling.preview.Preview annotation on the classpath, so previews declared in commonMain are captured too.
Robolectric or Desktop — trade-offs
Fidelity: Robolectric renders with the Android framework; desktop renders with the host's Skia, so the same preview produces different images — goldens are per-platform.
Speed: desktop tests run roughly 4–6x faster than the Robolectric ones (benchmark).
Adoption cost: requires a Kotlin JVM target — a Kotlin Multiplatform
jvm()target or a plainorg.jetbrains.kotlin.jvmproject. For an Android-only project that means a KMP migration first — stick with the Robolectric preview tests there. Desktop tests shine for already-multiplatform code and Desktop-only apps.
Enable it in your build.gradle.kts:
Add the dependencies to the JVM target's test source set. If your previews use the Roborazzi marker annotations (@RoboPreviewExclude, @RoboComposePreviewOptions, ...), also add roborazzi-annotations to the source set that declares the previews (usually commonMain — test dependencies do not flow into main source sets):
Plain JVM projects (org.jetbrains.kotlin.jvm) are also supported: add the same dependencies with testImplementation(...) and use the Jvm task names (recordRoborazziJvm, compareRoborazziJvm, verifyRoborazziJvm).
Then run the desktop Roborazzi tasks:
Note: ComposablePreviewScanner is published with JVM 17 metadata, so the desktop target needs to target JVM 17 (or relax the test classpath's TargetJvmVersion attribute). See the desktop multiplatform sample for a complete setup, including manual usage of the tester API without the generator.
Screenshot naming and mixed modules
Desktop preview screenshots use the same file names as the Robolectric preview tests (fully qualified class name + method name + preview parameter suffix), so the same preview produces the same file name on both platforms. If one module enables both generateComposePreviewRobolectricTests and generateComposePreviewDesktopTests, the two sets of screenshots would overwrite each other in the shared output directory, so Roborazzi fails with a configuration error unless separateOutputDirs is enabled, which gives each task its own subdirectory.
Customizing the desktop tester
DefaultDesktopComposePreviewTester accepts a Capturer whose receiver is the raw ComposeUiTest scope, so anything possible inside runDesktopComposeUiTest — clock control, interactions, wrapping the content in a theme — stays possible:
Reference your tester in the Gradle configuration with testerQualifiedClassName = "com.example.MyDesktopPreviewTester" (the class needs a parameterless constructor). If you need to change scanning or file naming as well, implement DesktopComposePreviewTester by delegating to the default tester and override the corresponding method (testParameters()/test()).
To wrap each generated test in a JUnit TestRule (a TestWatcher, retry rule, etc.), override options() and provide a testRuleFactory:
Unlike the Robolectric tester there is no compose rule factory: Compose Desktop's test harness is function-scoped (runDesktopComposeUiTest), not rule-based.
Feature parity with the Android preview support
Feature | Android (Robolectric) | Compose Desktop |
|---|---|---|
Generated preview tests | ✅ | ✅ |
| ✅ | ✅ |
| ✅ | ✅ |
| ✅ | ✅ |
| ✅ | ✅ |
Custom JUnit | ✅ | ✅ |
Compose rule factory ( | ✅ | Not applicable (function-scoped harness) |
| ✅ (see below) | ✅ |
| ✅ | Not applicable (no device configuration on desktop) |
| ✅ | Not applicable |
On Compose Desktop the @Preview annotation options are applied as follows:
widthDp/heightDp: the preview is wrapped in a fixed-size box (density is1, so 1dp equals 1px). When neither is specified the preview still renders wrap-content.fontScale: applied throughLocalDensity(density stays1), becauseDeviceConfigurationOverride.FontScaleis unsupported on desktop.showBackground/backgroundColor: draws a background behind the preview, defaulting to white whenshowBackground = truebut no color is given.locale: setsjava.util.Locale.getDefault()for the capture and restores it afterwards. Accepts"ja","ja-rJP", and"ja-JP"forms.uiMode: only the night bit is honored (dark mode viaLocalSystemTheme); other configuration bits are ignored.device: not applicable, as desktop has no device configuration.
Annotation-based Capture Control
To enable fine-grained control over screenshot timing in Compose Previews, add the annotations dependency:
Use @RoboComposePreviewOptions to configure time-based captures:
This annotation enables capturing screenshots at specific time intervals, particularly useful for testing animated components or delayed state changes.
PreviewWrapper support
Previews annotated with @PreviewWrapper (Compose UI 1.11+) are automatically wrapped by ComposablePreviewScanner 0.9.0 or later, so the wrapper's content, such as a theme or background, appears in the screenshots without any extra setup:
Manually adding Compose Preview screenshot tests
Roborazzi provides a helper function for ComposablePreviewScanner. You can add the following dependency to your project to use the helper function:
testImplementation("io.github.takahirom.roborazzi:roborazzi-compose-preview-scanner-support:[version]")
Then you can use the ComposablePreview<AndroidPreviewInfo>.captureRoboImage() function to capture the Composable Preview using the settings in Preview annotations. To obtain the ComposablePreview object, please refer to ComposablePreviewScanner.
The supported @Preview annotation options
Currently, we don't support all the annotation options provided by the Compose Preview. You can check the supported annotations in the source code. We are looking forward to your contributions to support more annotation options.