To start snapshot testing in Java or any JVM language, all you need is to add a single dependency.
Selfie snapshot testing works with the following JVM test runners:
Disk snapshots can be used with any JVM language, but inline literal snapshots only work if the test code is written in:
Adding support for other languages is straightforward, PRs are welcome!
If you're using Maven, add the following dependency to your pom.xml
file:
<dependency>
<groupId>com.diffplug.selfie</groupId>
<artifactId>selfie-runner-junit5</artifactId>
<version>${ver_SELFIE}</version>
<scope>test</scope>
</dependency>
Replace ver_SELFIE
with the latest available version of selfie.
For Gradle users, add this to your build.gradle
file:
dependencies {
// ... other dependencies
testImplementation "com.diffplug.selfie:selfie-runner-junit5:$ver_SELFIE"
}
test {
useJUnitPlatform()
environment project.properties.subMap(["selfie"]) // optional, see "Overwrite everything" below
inputs.files(fileTree("src/test") { // optional, improves up-to-date checking
include "**/*.ss"
})
}
or this to build.gradle.kts
:
dependencies {
testImplementation("com.diffplug.selfie:selfie-runner-junit5:${project.properties["ver_SELFIE"]}")
}
tasks.test {
useJUnitPlatform()
environment(properties.filter { it.key == "selfie" }) // optional, see "Overwrite everything" below
inputs.files(fileTree("src/test") { // optional, improves up-to-date checking
include("**/*.ss")
})
}
Replace ver_SELFIE
with the latest available version of selfie.
If you haven't seen the GIF on our GitHub, you might want to watch that first (give us a ⭐ while you're at it 😉).
Let's say we have the following test code:
expectSelfie(List.of(1, 2, 3).toString()).toBe_TODO();
If you run this test, selfie will rewrite your sourcecode to be this:
expectSelfie(List.of(1, 2, 3).toString()).toBe("[1, 2, 3]");
Now, let's change the code to this:
expectSelfie("oops").toBe("[1, 2, 3]");
When we run the test, we will get a failure, and the failure message will be:
Snapshot mismatch
- update this snapshot by adding `_TODO` to the function name
- update all snapshots in this file by adding `//selfieonce` or `//SELFIEWRITE`
As you can see, we have three options:
toBe
with toBe_TODO
(you can leave or remove the "[1, 2, 3]"
, makes no difference)
_TODO
//selfieonce
anywhere in the file
//selfieonce
after it has done so//SELFIEWRITE
anywhere in that file
//SELFIEWRITE
yourselfTo store a snapshot on disk, swap toBe
for toMatchDisk
:
expectSelfie(List.of(1, 2, 3).toString()).toMatchDisk_TODO();
This will create a file called SomethingOrOther.ss
in the same directory as your test. It will also rewrite the test source to:
expectSelfie(List.of(1, 2, 3).toString()).toMatchDisk();
Just like inline literal snapshots, you can use _TODO
, //selfieonce
, and //SELFIEWRITE
to control how the snapshots are written and updated. You don't have to use _TODO
if you have the //selfieonce
or //SELFIEWRITE
comments in your file.
If you want the disk snapshots to live in a different folder, set snapshotFolderName
using SelfieSettings.
The nice thing about //SELFIEWRITE
is that all of your snapshots will update on every run, which makes it easy to explore — like multiassert on steroids. The bad thing about //SELFIEWRITE
is that all of the tests always pass, even if the snapshots actually change on every run.
For example, you might have a realtime timestamp or a random port number embedded in a snapshot. Randomness and realtime cannot be present in a repeatable assertion, and you might not realize that a tiny part of a large snapshot is changing while you're in //SELFIEWRITE
mode.
For this reason, it is critical that a CI server should always run in readonly
mode. No action is needed on your part, selfie automatically puts itself into readonly
mode if the CI=true
environment variable is present, which is true for all popular CI systems.
When in readonly
mode, selfie not only refuses to update any snapshots, it also fails the build if _TODO
, //selfieonce
, or //SELFIEWRITE
are present in the sourcecode, even if the snapshots were correct. Writing snapshots is a strictly private affair 😏.
Selfie has three modes:
interactive
, the default mode, which we discussed in the quickstartreadonly
, the default mode if CI=true
, where no snapshots can be writtenoverwrite
, where every snapshot is overwritten, regardless of whether it is _TODO
or notTo set the mode, you set the selfie
or SELFIE
environment variable or system property to either interactive
, readonly
, or overwrite
. But in the vast majority of cases, it's best to leave it alone and let the defaults do their thing.
[MAVEN]
user@machine repo % mvn test -Dselfie=overwrite
[GRADLE (only works if you followed the install instructions above re: environment)]
user@machine repo % ./gradlew test -Pselfie=overwrite
All of the examples so far have asserted on Strings. You can also do inline literal assertions on primitive values, and disk assertions on byte arrays:
expectSelfie(10/4).toBe(2);
expectSelfie((10/4) == 2).toBe(true);
expectSelfie(TimeUnit.DAYS.toMillis(365*1_000_000L)).toBe(31_536_000_000_000_000L);
expectSelfie(new byte[100]).toMatchDisk();
But the real power of selfie is asserting on arbitrary objects using facets, which are covered in the advanced section.
Full API documentation is available at kdoc.selfie.dev. For details on threading, see threading details.
.toBe_TODO()
or .toBe_TODO(argumentIsIgnored)
.toMatchDisk_TODO()
//selfieonce
_TODO
or not//SELFIEWRITE
_TODO
or notSELFIE
environment variable or system property
interactive
, defaultreadonly
, default if CI
environment variable is true
overwrite
, all snapshots can be overwrittenCamera
and Lens
are covered in the advanced section.Pull requests to improve the landing page and documentation are greatly appreciated, you can find the source code here.