Roborazzi Documentation

repository·main·Indexed 21 days ago

https://github.com/takahirom/roborazzi

A screenshot testing library for Android that integrates with Robolectric to perform visual regression testing on JVM-based Android tests. It supports capturing screenshots, GIFs, and experimental UI videos, as well as generating machine-readable UI tree JSON dumps and Set-of-Mark annotated images for AI agents. Requires Robolectric 4.10 alpha 1 or later and @GraphicsMode(GraphicsMode.Mode.NATIVE).

Tokens
48.1K
Snippets
130
Records
163
Agent score
73%

What's inside Roborazzi

  1. What is Roborazzi and why use it?

    main

    Roborazzi is a screenshot testing library that makes JVM Android Integration Tests visible. It integrates with Robolectric (specifically supporting Robolectric Native Graphics (RNG)) to enable efficient screenshot testing on the JVM.

    Key Benefits

    • Visual Validation: Detects visual regressions and validates app appearance more efficiently than manual assertion statements.
    • JVM-based Testing: Unlike Instrumentation tests (which run on real devices/emulators in androidTest/ and are prone to environment-related false negatives), Roborazzi runs JVM tests (local tests in test/) on a developer's PC or CI environment.
    • Robolectric Integration: Unlike Paparazzi, which is incompatible with Robolectric, Roborazzi works alongside Robolectric. This allows you to use tools like Hilt and interact with complex Android components within your JVM tests.

    Comparison: Paparazzi vs. Roborazzi

    FeaturePaparazziRoborazzi
    JVM VisualizationYesYes
    Robolectric CompatibilityNoYes
    Hilt/Component InteractionLimitedHigh (via Robolectric)
  2. Overview of Roborazzi screenshot testing

    main

    Roborazzi is a screenshot testing library for Android that executes on the JVM using Robolectric Native Graphics (RNG). This allows screenshot tests to run as standard JVM unit tests without requiring a physical device or an emulator.

    It supports:

    • Plain Android Views
    • Jetpack Compose (including Compose Previews)
    • Compose Multiplatform (iOS and Desktop/JVM)

    Typical Workflow:

    1. Record baseline images using the record task (e.g., ./gradlew recordRoborazziDebug).
    2. Compare/Verify against baselines using the compare or verify tasks (e.g., compareRoborazziDebug or verifyRoborazziDebug). Note that task names vary depending on your build variant.
  3. Overview of Roborazzi screenshot testing

    main

    Roborazzi is a tool designed to make JVM Android Integration Tests visible by enabling screenshot testing.

    Key Features and Benefits:

    • Robolectric Integration: Unlike Paparazzi, Roborazzi integrates with Robolectric, allowing tests to run with Hilt and interact with complex components.
    • JVM Testing: It focuses on JVM tests (local tests in the test/ directory) rather than Instrumentation tests (in androidTest/). This avoids the flakiness and environmental issues often associated with running tests on real devices or emulators.
    • Visual Validation: It provides a way to efficiently detect visual issues and validate app appearance and functionality through screenshots, which is often faster than writing numerous manual assertion statements.
  4. Roborazzi IntelliJ IDEA and Android Studio Plugin

    main
    The Roborazzi plugin for IntelliJ IDEA and Android Studio allows you to automatically view and verify screenshots related to your Roborazzi tests directly within your IDE, streamlining the visual verification workflow.
  5. Understand Desktop Preview screenshot naming

    main

    Desktop preview screenshots follow the same naming convention as Robolectric preview tests to ensure cross-platform consistency. The filename is constructed using the Fully Qualified Class Name (FQCN) of the class containing the preview, followed by the preview name.

    Format: [FQCN].[PreviewName].png Example: com.example.HomeScreenKt.LoadingPreview.png

    This naming scheme allows for easy comparison between Android and Desktop screenshots for the same composable.

  6. Enable UI tree dump for Compose Desktop

    main

    Compose Desktop supports full UI tree dumping. You can enable this via uiTreeDumpOptions in the captureRoboImage call or via a Gradle project property.

    When enabled, Roborazzi writes:

    1. A MyTest.uitree.json sidecar file.
    2. An annotated image MyTest.annotated.png (or MyTest_actual.annotated.png during comparison).

    How to enable:

    • Via Code: Pass uiTreeDumpOptions to captureRoboImage.
    • Via CLI: Pass -Proborazzi.dumpUiTree=true to your Gradle command.
  7. Use Annotated Images (Set-of-Mark) for UI verification

    main

    When UI tree dumping is enabled, Roborazzi automatically generates an annotated image (e.g., MyTest.annotated.png) next to your screenshot. This image overlays numbered boxes on every node that has an n value in the JSON.

    Purpose

    This follows the "Set-of-Mark" prompting technique, allowing vision-language models (AI agents) to refer to specific UI elements by their number (e.g., "Element #3 is misaligned") rather than guessing coordinates.

    Usage Notes

    • The number on the box matches the n field in the .uitree.json file.
    • The annotated image is a display artifact only. It is never used for comparison and never causes a test to fail.
    • To disable the annotated image, use UiTreeDumpOptions(annotateImage = false).
    • File naming:
      • Record: MyTest.annotated.png
      • Compare/Verify: MyTest_actual.annotated.png
  8. Use RoborazziRule for advanced configuration

    main

    While captureRoboImage() can be used standalone, RoborazziRule is an optional JUnit rule that provides advanced control over the screenshot lifecycle and output.

    Key features of RoborazziRule:

    1. Context Injection: Provides RoborazziOptions and outputDirectoryPath to captureRoboImage() calls.
    2. Automatic Capture Types: Can automatically capture screenshots for every test based on the captureType setting.

    RoborazziRule.Options properties:

    • captureType: Determines what is captured per test:
      • None (default): Rule only provides context.
      • LastImage: Captures the last image of the test.
      • AllImage: Captures an image for every layout change (e.g., TestClass_method_0.png).
      • Gif: Captures an animated GIF.
      • Note: Image-generating types can be set to onlyFail = true to capture only on test failure.
    • outputDirectoryPath: Custom directory for output files.
    • outputFileProvider: A lambda to customize how files are named.
    • roborazziOptions: Custom RoborazziOptions to be used by the capture functions.
    @get:Rule
    val roborazziRule = RoborazziRule(
        options = Options(
          outputDirectoryPath = "$DEFAULT_ROBORAZZI_OUTPUT_DIR_PATH/custom_dir",
          outputFileProvider = { description, outputDirectory, fileExtension ->
            File(outputDirectory, "custom-${description.testClass.name}.${description.methodName}.$fileExtension")
          }
        ),
      )
  9. Use Annotated Images (Set-of-Mark) for AI verification

    main

    Roborazzi can generate an annotated image (MyTest.annotated.png) that overlays numbered boxes on UI elements. The number on the box matches the n field in the uitree.json sidecar.

    This 'Set-of-Mark' approach allows AI agents to unambiguously refer to specific UI regions (e.g., "element #3") and look up its exact bounds, properties, and actions in the JSON file.

    To disable annotated images: Pass UiTreeDumpOptions(annotateImage = false) in your RoborazziOptions.

  10. Use Annotated Images (Set-of-Mark) for AI Agents

    main

    Roborazzi can generate 'Set-of-Mark' annotated images to help vision-language models (AI agents) identify UI elements. These images overlay numbered marks on image regions, allowing an agent to refer to an element by its number (e.g., "element #3").

    Key Details:

    • Naming Convention: The annotated image is a sidecar to your screenshot. For a record named MyTest.png, the annotated version is MyTest.annotated.png. For a comparison named MyTest_actual.png, it is MyTest_actual.annotated.png.
    • Mapping: Each box's number matches the n value in the generated UI tree JSON, allowing agents to look up exact bounds, properties, and actions.
    • Lifecycle: Annotated images are display artifacts of the current run only. They are never used for comparison, never cause test failures, and are never treated as golden images.
    • Opt-out: You can disable this feature using UiTreeDumpOptions(annotateImage = false).
  11. Use @PreviewWrapper for Automatic Theme Wrapping

    main

    If you use Compose UI 1.11+ and the @PreviewWrapper annotation, Roborazzi (via ComposablePreviewScanner 0.9.0+) will automatically wrap your previews in the provided wrapper. This allows you to apply themes or backgrounds to all previews declared with that wrapper without extra configuration.

    class MyWrapperProvider : PreviewWrapperProvider {
      @Composable
      override fun Wrap(content: @Composable () -> Unit) {
        MyTheme { content() }
      }
    }
    
    @PreviewWrapper(MyWrapperProvider::class)
    @Preview
    @Composable
    fun WrappedPreview() { ... }
  12. Use RoborazziRule for advanced output management

    main

    While captureRoboImage() can be used standalone, RoborazziRule (a JUnit rule) provides advanced control over the screenshot lifecycle and output.

    Key features of RoborazziRule:

    1. Context Injection: Provides RoborazziOptions and outputDirectoryPath to captureRoboImage() calls.
    2. Automatic Capture: Can automatically capture screenshots for every test based on the captureType option.

    RoborazziRule.Options properties:

    • captureType: Determines what is captured per test:
      • None (default): Rule only provides context.
      • LastImage: Captures the last image change.
      • AllImage: Captures an image for every layout change (e.g., TestClass_method_0.png).
      • Gif: Captures an animated GIF.
      • Note: Image-generating types support onlyFail = true to capture only on test failure.
    • outputDirectoryPath: The directory where output files are saved.
    • outputFileProvider: A lambda to customize file naming logic.
    • roborazziOptions: Custom RoborazziOptions to be used during capture.
    @get:Rule
    val roborazziRule = RoborazziRule(
      options = Options(
        outputDirectoryPath = "$DEFAULT_ROBORAZZI_OUTPUT_DIR_PATH/custom_output_dir",
        outputFileProvider = { description, outputDirectory, fileExtension ->
          File(outputDirectory, "custom_prefix-${description.testClass.name}.${description.methodName}.$fileExtension")
        }
      ),
    )