Android Snapshots
A full end-to-end snapshot testing suite, ready to go in 10 minutes
Only compatible with Emerge Gradle plugin 3.0+
You must be using snapshots 1.0.0 with any 3.0.0+ version of Emerge's Gradle Plugin for snapshots to properly work. See the emerge-android repo for latest snapshot versions.
Setup (~10 min)
Emerge snapshotting requires the usage of Emerge's Gradle plugin and the com.emergetools.snapshots:snapshots
library. That's it!
Add Emerge Gradle Plugin
See Gradle Plugin (Android) for full gradle plugin setup instructions - for a quick setup, add the Emerge plugin to your app module's build.gradle.kts
file:
plugins {
id("com.emergetools.android")
}
emerge {
apiToken.set(System.getenv("EMERGE_API_TOKEN"))
}
The above setup assumes you've created an API token and set as the EMERGE_API_TOKEN
env variable. Full API key instructions can be found at obtain an API key.
Add Snapshot SDK
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>")
}
Run preflight check
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.
Upload to Emerge
The Emerge Gradle plugin offers a single command to build & upload snapshot tests to Emerge. Emerge will handle generating, storing, and diffing the snapshots against a base build for you.
./gradlew :app:emergeUploadSnapshotBundleDebug
Once uploaded, snapshots and diffs are viewable directly in the Emerge UI. You can find a link to the build in the Gradle output.
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.
How it works
Under the hood, Emerge is instrumenting the preview annotations to expose them at runtime in your app's debug build. Emerge can then find and snapshot all previews from the instrumented build, with no work on your part.
Supported types
Composables
Emerge's gradle plugin automatically instruments snapshot tests for compose previews in the main
source set.
// /src/main/com/myapp/MyComposable.kt
@Preview
@Composable
fun MyComposablePreview() {
MyComposable(
text = "Hello, World!"
)
}
Current limitations
@PreviewParameter
is not currently supported, but long term plans to support it are planned. For mocking data, instantiate data directly in the composable preview function.
Legacy views
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) {
// ...
}
Variant support
As of the latest snapshots release, Emerge currently supports all @Preview
annotation parameters for variants except:
apiLevel
- per-previewapiLevel
support is not yet possible, but thesnapshots.apiVersion
gradle property can be used to selectapiLevel
for all previews. See Android API levels for details.showSystemUi
support is planned in a future release of Emerge snapshotswallpaper
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, and one with 1.5f
font scale.
Multipreview support
Emerge has full support for Multipreview annotations and even stacked multipreviews.
As an example, the following annotation can be used to generate multiple previews when the custom annotation is used in place of multiple @Preview
annotations.
// src/main/com/myapp/SnapshotTestingPreview.kt
@Preview
@Preview(fontScale = 1.5f)
@Preview(uiMode = UI_MODE_NIGHT_YES)
annotation class SnapshotTestingPreview
Using SnapshotTestingPreview
, the following preview will produce 3 variants, a default (@Preview
annotation), a large font (@Preview(fontScale = 1.5f)
) and a dark mode (@Preview(uiMode = UI_MODE_NIGHT_YES)
).
// src/main/com/myapp/MyComposable.kt
@SnapshotTestingPreview
@Composable
fun MyComposablePreview() {
MyComposable(
text = "Hello, World!"
)
}
Android API levels
Emerge supports specifying the system API level for snapshotting all previews through the snapshots.apiVersion
Gradle plugin property
emerge {
// ...
snapshots {
apiVersion.set(31) // must be 29, 31, 33 or 34
}
}
By default, Emerge snapshots using API level 34 (Android 14). A full list of API levels can be found here. Currently, Emerge only supports API levels 29, 31, 33 and 34. Should you have a use case for other API levels, let us know and we can likely accommodate!
Long term, Emerge plans to support the @Preview
annotation's apiLevel
parameter for more granular API level support.
Including Preview snapshots from a non-main source set
Emerge will automatically generate snapshot tests for all composable previews and their supported variants in the main
source set by default, but sometimes creating previews for snapshot testing in a separate source set is more desired.
Emerge uses your main source set as the source of truth, so simply add your snapshot testing source set to your target variant's build configuration.
plugins {
id("com.emergetools.android")
}
emerge {
// ..
}
android {
// ..
sourceSets {
// Apply for any variant you'd like, Emerge will generate a snapshot task for all debuggable variants
getByName("debug") {
kotlin.srcDirs(
"src/main/kotlin",
"src/snapshots/kotlin", // Will include Previews from the snapshots source set
)
}
}
}
dependencies {
androidTestImplementation("com.emergetools.snapshots:snapshots:<latest_version>")
}
As an example, assuming the following preview lives in the snapshot
source set:
// /src/snapshot/kotlin/com/myapp/MyComposablePreviewTest.kt
@Preview
@Composable
fun MyComposablePreviewTest() {
MyComposable(
text = "Hello, World!"
)
}
Emerge will automatically generate a preview snapshot test for MyComposablePreviewTest()
.
Additional resources
For details on how Emerge manages and diffs snapshots, see Reviewing diffs.
Full source code and SDK documentation is available in the emerge-android repository.
Updated 19 days ago