All-in-one Android snapshot testing with 2 lines of code.
Emerge offers a full-suite snapshotting solution on Android, allowing for snapshotting of Composables, Activities, and Views. Emerge handles everything for you, including:
- Automatic snapshot testing of Compose Previews
- Fully managed snapshot generation in CI
- Hosted UI for comparing diffs, sharing snapshots, and approving changes
- Snapshot image file storage
- Image comparisons & diffs
- Posting status checks & comments to pull requests in GitHub or GitLab
And many more features!
This is a quick setup guide to set up end-to-end snapshots in about 10 minutes. For a full guide on setting up Emerge snapshots, see the full documentation.
Add the Emerge Gradle plugin to your application's build.gradle.kts
file:
plugins {
id("com.emergetools.android")
}
emerge {
// If not explicitly set, Emerge uses the EMERGE_API_TOKEN env variable
apiToken.set(System.getenv("EMERGE_API_TOKEN"))
}
See gradle-plugin for more information.
Emerge snapshot SDKs are published to Maven Central and should be added as dependencies to your
app's build.gradle.kts
file.
Compose @Preview
snapshot generation relies on a Gradle plugin instrumentation to modify Compose
Previews to be visible at runtime. Our snapshot SDK can then handle everything else, invoking the
Compose Preview and saving the resulting snapshot image.
dependencies {
// ..
androidTestImplementation("com.emergetools.snapshots:snapshots:<latest_version>")
}
Let's check that everything's set up properly by running the snapshots preflight task. A preflight check is automatically generated for all debug variants of your app:
./gradlew :<app>:emergeSnapshotsPreflightDebug
The preflight task will give detailed output about the Emerge snapshots integration:
› ./gradlew :app:emergeSnapshotsPreflightDebug
> Task :app:emergeSnapshotsPreflightDebug
╔════════════════════════════════════════════════╗
║ Snapshots preflight check was successful (3/3) ║
╠════════════════════════════════════════════════╝
╠═ ✅ Emerge API token set
╠═ ✅ Snapshots enabled
╚═ ✅ Snapshots SDK is an androidTestImplementation dependency
╔═════════════════════════════════════╗
║ VCS Info check was successful (4/4) ║
╠═════════════════════════════════════╝
╠═ ✅ SHA: 123456789..
╠═ ✅ Base SHA: 987654321..
╠═ ✅ Branch name: main
╚═ ✅ PR number: 123
Snapshots preflight was successful!
Check snapshots locally with ./gradlew :app:emergeLocalSnapshotsDebug (make sure to have an emulator or connected device running)
Upload & run snapshots on Emerge with ./gradlew :app:emergeUploadSnapshotBundleDebug
If there are any issues or warnings, the preflight task should help you identify and address them before uploading to Emerge.
The Emerge Gradle plugin offers a single command to build & upload snapshot tests to Emerge.
./gradlew :app:emergeUploadSnapshotBundleDebug
Emerge will handle generating, storing, and diffing the snapshots against a base build by git information for you.
Once uploaded, snapshots and diffs are viewable directly in the Emerge UI. You can find a link to the build in the Gradle output.
For integrating snapshots in CI, Emerge recommends the following order:
- Adding snapshots to the main branch. This ensures all PRs have a base build to diff against.
- Adding snapshots to all commits on PRs. This ensures that snapshots are generated for every commit, allowing for diffing any changes.
Tip
👍 That's it! Emerge recommends running snapshot tests on every commit. Emerge will handle generating, storing and diffing based on Git information. Continue reading for full details on how Emerge generates and manages snapshots.
Emerge's Gradle plugin (3.0+) automatically snapshots all @Preview
annotated
composables in your main
source set.
No additional setup is required!
// src/main/com/myapp/MyComposable.kt
@Preview
@Composable
fun MyComposablePreview() {
MyComposable(
text = "Hello, World!"
)
}
@PreviewParameter
annotated arguments
like the below example are not yet supported.
@Preview
@Composable
fun MyComposablePreview(
// NOT YET SUPPORTED
@PreviewParameter(UserPreviewParameterProvider::class) user: User
) {
MyComposable(user)
}
Emerge currently supports all @Preview
annotation parameters except for wallpaper
and showSystemUi
.
Emerge will automatically generate a snapshot test for each Preview annotation present. For example, for the following composable:
// src/main/com/myapp/MyComposable.kt
@Preview
@Preview(fontScale = 1.5f)
@Composable
fun MyComposablePreview() {
MyComposable(
text = "Hello, World!"
)
}
Emerge will generate two snapshots, one default (no-arg @Preview
), and one with 1.5f
font
scale (@Preview
with fontScale
param).
Not all Previews might need to be snapshotted. To ignore a preview from snapshotting, add
the @IgnoreEmergeSnapshot
annotation to the preview function.
@Preview
@IgnoreEmergeSnapshot
@Composable
fun MyComposablePreview() {
MyComposable(
text = "Hello, World!"
)
}
You'll need to add a dependency on the snapshots-annotations
artifact to use the
@IgnoreEmergeSnapshot
annotation. This is a lightweight library only containing annotations
the snapshots-processor
leverages.
dependencies {
debugImplementation("com.emergetools.snapshots:snapshots-annotations:{latest_version}")
}
Emerge can snapshot Compose previews with any visibility, like private
or internal
. But for some
projects, you might not want to snapshot private previews for various reasons.
Emerge's gradle plugin has an option to include private previews, which can be set to false to
disable snapshotting private
visibility preview functions.
emerge {
// ...
snapshots {
includePrivatePreviews.set(false) // true by default - set to false to exclude private previews from snapshotting
}
}
Emerge is 100% invested in Compose, but we know your codebase might not be fully there yet.
Emerge can snapshot legacy views through Compose Previews. Wrap the legacy view in an AndroidView
(docs) and add to any Preview!
@Preview
@Composable
fun LegacyTitleViewPreview() {
AndroidView(
factory = { context: Context ->
LegacyTitleView(context).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
}
},
update = { view ->
// Configure legaacy view here.
view.setTitle("Hello, Legacy View!")
view.setSubtitle("I'm a legacy view in a Compose preview")
}
)
}
class LegacyTitleView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {
// ...
}
The Emerge Gradle plugin provides a task to generate snapshots locally for snapshot tests.
Add the Emerge gradle plugin to your app-level build.gradle(.kts)
file:
plugins {
id("com.emergetools.android")
}
emerge {
// Optional
snapshots {
tag.set("snapshots") // To ensure Emerge size dashboard is not mixed with snapshot builds
}
}
Snapshot support was added in Emerge gradle plugin v2.0.0, versions below 2.0.0 do not support snapshots.
Then, run the emergeLocalSnapshots<variant>
task to generate snapshots. You'll need a connected
device or emulator actively running and ANDROID_HOME
set to the location of the Android SDK is on
your PATH
.
./gradlew :snapshots:snapshots-sample:emergeLocalSnapshotsDebug
Emerge supports generating, hosting & diffing screenshots as part of our Cloud snapshotting service.
Using the Emerge Gradle plugin, you can upload snapshots to the cloud using
the emergeUploadSnapshotBundle<variant>
task.
./gradlew :snapshots:snapshots-sample:emergeUploadSnapshotBundleDebug
For full documentation on Emerge's cloud snapshotting service, see the Emerge Snapshots documentation.
To properly diff snapshots, Emerge needs to know which snapshots to compare against. This is done using Git information, which is set automatically for you.
See gradle-plugin documentation for a full list of configuration VCS configuration options.
For more details on Emerge's snapshotting service, see the full documentation.
Artifact | Description | Latest | Min SDK |
---|---|---|---|
com.emergetools.snapshots:snapshots |
Snapshot testing SDK | 23 | |
com.emergetools.snapshots:snapshots-annotations |
Additional snapshot annotations | 23 |
- Update the
emerge-snapshots
version in/gradle/libs.versions.toml
- Update the
/snapshots/CHANGELOG.md
gt c -am "Prepare for Snapshots release X.Y.Z"
(where X.Y.Z is the version set in step 1)- Alt
git add *
git commit -m "Prepare for Snapshots release X.Y.Z"
gt ss
- Alt:
git push
- Open PR
- Get PR approved and merge
- Create a new release on GitHub
- Tag version
snapshots-vX.Y.Z
- Release title
Snapshots vX.Y.Z
- Paste the content from
/snapshots/CHANGELOG.md
as the description
The snapshots-release
workflow will automatically publish the new version to Sonatype upon new
release publish.
From there, the release will need to be promoted to the main repository from the Sonatype UI.