| |

Compose Screenshot Testing – from Setup to CI/CD Integration

Compose Screenshot Testing from Setup to CI/CD Integration

Imagine catching UI regressions automatically before your users even notice. With screenshot testing in Jetpack Compose, you capture your app’s visual output as images and compare them against a verified baseline.

This means any unintended change, whether it’s a shifted element or a miscolored component, is flagged immediately.
In short, screenshot tests act as an automated quality gate for your UI, ensuring every update looks exactly as intended.

flowchart TD;
    A["Create directory structure for screenshot tests"]
    B["Implement Screenshot Test"]
    C["Run Gradle task to generate baseline images"]
    D["Run tests to validate screenshots"]
    
    A --> B
    B --> C
    C --> D

1. Setup and Requirements

Apply plugin and import dependencies from Android documentation, if you need additional reference you can check the changes from this Pull Request highlights the changes accurately.

I’m currently using below version.

[versions]
screenshot = "0.0.1-alpha07"

These are the minimum requirements according to documentation.

  • Kotlin 1.9.20 or higher.
  • Android Gradle 8.5.0-beta01 or higher.

2. Implementing Compose Screenshot Tests

2.1 Environment Setup & Directory Structure

Create a source set for screenshot tests in your module’s directory structure as below.
This isolated source keeps screenshot test code separate from unit and instrumented tests (If you have any).

app/
└── src/
├── main/
└── screenshotTest/
└── kotlin/
└── com/
└── developersbreach/
└── composeactors/
└── screenshotTests/
└── SearchScreenScreenshotTest.kt
Compose Screenshot Testing – from Setup to CI/CD Integration

2.2 Creating your Screenshot test from Preview

We can reuse the existing preview defined in our composable screens which are created during development.
By directly calling preview function you ensure that the exact UI rendered during development is captured for comparison for future changes as reference.

package com.developersbreach.composeactors.screenshotTests

import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import com.developersbreach.composeactors.ui.screens.search.SearchScreenUILightPreview

@Preview
@Composable
private fun SearchScreenUIPreviewTest() {
    SearchScreenUILightPreview()
}

Preview which is part of your composable screen in SearchScreenUI :

// SearchScreenUI.kt :
@Composable
fun SearchScreenUI(
    navigateUp: () -> Unit,
    navigateToSearchBySearchType: (Int) -> Unit,
    searchHint: String,
    onSearchQueryChange: (query: String) -> Unit,
    data: SearchDataType,
) {
    // Screen contents
}

@Preview(showBackground = true)
@Composable
fun SearchScreenUILightPreview() {
    ComposeActorsTheme(darkTheme = false) {
        SearchScreenUI(
            navigateUp = {},
            navigateToSearchBySearchType = {},
            searchHint = stringResource(R.string.hint_search_query_actors),
            onSearchQueryChange = {},
            data = ActorSearch(
                personList = fakePersonsList(),
                isSearchingResults = false
            )
        )
    }
}

The test preview calls our custom preview function ensuring the UI is rendered using your actual app theme and data.

2.3 Generating Baseline Images

After setting up the test class, the next step is to generate reference images for each preview. These images serve as a baseline for future comparisons.

Gradle command to generate the reference images :

gradlew updateDebugScreenshotTest

After running this task, reference images are generated under:

app/src/debug/screenshotTest/reference/com/developersbreach/composeactors/screenshotTests/

2.4 Generating a test report

Next step is to run the validation task to capture a new screenshot and compare it with the baseline image.
For that we can run this command :

gradlew validateDebugScreenshotTest

This validation task will generate an HTML report located at {module}/build/reports/screenshotTest/preview/{variant}/index.html. This report will help you track any visual changes between the current and reference images.

This image displays the XML report that provides detailed results of the screenshot test, showing the test case execution status and time taken.

This image shows the HTML report highlighting visual changes between the current and reference images:

Voila!
You’ve successfully created and validated your first screenshot test in Jetpack Compose. Now, your UI is more reliable, and visual bugs are caught before your users even notice!

3. Running Screenshot Tests in CI/CD

While we run the tests locally we use gradle command as below

./gradlew validateDebugScreenshotTest

Say you already have other Gradle tasks configured in your GitHub Actions workflow, you can easily join the screenshot testing task with your existing commands.
If your workflow script already runs unit tests and instrumented tests, simply append the new task to your Gradle command.

script: |
./gradlew testDebugUnitTest validateDebugScreenshotTest connectedDebugAndroidTest --stacktrace
  • testDebugUnitTest runs your unit tests.
  • validateDebugScreenshotTest generates and compares Compose screenshot images against your reference images.
  • connectedDebugAndroidTest runs your instrumented tests on the emulator.

You can find the whole workflow file here, the key script would look as below, where it creates a virtual android device with mentioned attributes for device configuration and runs the mentioned gradle commands.

- name: Create Android Virtual Device and run all tests
  uses: reactivecircus/android-emulator-runner@v2
  id: testing
  with:
    api-level: 29
    target: google_apis
    arch: x86_64
    force-avd-creation: true
    emulator-options: -wipe-data -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
    disable-animations: false
    script: |
      ./gradlew testDebugUnitTest validateDebugScreenshotTest connectedDebugAndroidTest --stacktrace

4. Internals and failing tests

4.1 How Screenshot Testing Works Internally

When a screenshot test runs, Compose renders UI from @Preview functions into a bitmap image. This snapshot represents the current visual state of your component. If the images don’t match, the test fails and produces a report to help you compare and find the differences.

4.2 When Will a Screenshot Test Fail?

Have a look at both of the images, Reference and Actual image you will notice there is a little bit more padding to the left side. Hence the test will fail because it checks pixel by pixel, which is referenced in Diff image.

Before and after compose test validation

The gradle execution task failure will point us at the problem.

A small shift can cause the diff to exceed the threshold, resulting in a test failure that alerts you to the visual inconsistency.

  • Unintended UI Changes:
    If a layout alteration, like a shifted button or changed padding causes the rendered image to differ significantly from the baseline.
  • Color or Font Variations:
    Changes in colors, font sizes, or themes that are not part of the intended design update will result in a failing diff.
  • Dynamic Content:
    UI elements that display dynamic data may cause inconsistent screenshots. Stabilizing such content is critical to avoid false failures. So keep it short to static content and have your previews with mock data.

Conclusion :

  • UI Verification:
    Screenshot tests capture the entire rendered UI and compare it pixel by pixel.
  • Automatic Regression Detection:
    They flag any unintended visual changes whether due to layout shifts, color changes, or environmental differences.
  • Prevents Regressions:
    As your app evolves, changes made to one part of the UI can unintentionally affect others. Screenshot testing prevents such regressions by continuously comparing new builds with previous versions.
  • Fast and Reliable:
    Running on the host JVM, these tests provide rapid feedback, ensuring your UI remains consistent across updates.
  • Integration:
    By reusing existing Compose previews, you maintain a single source of truth for your UI design.

References :

Compose Preview Screenshot Testing
Project with screenshots tests implementation

Author

Reviewer & Editor

Similar Posts

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.