snaptest

Snapshot tests for Scala

snaptest records what your tests produce as generated Scala files, and fails with a diff and a link to the line when it changes.

snapshots/HomePageTestSnapshots.scala

            

          
The entry snaptest writes

          

Plain strings, triple quotes, or a margin for values over many lines: snaptest picks the literal a reviewer reads fastest.

Any value, readable Scala

From test to snapshot

Write a test that returns a value.

Mix the snaptest trait of your framework into the suite, then call assertSnapshot or define a snapshotTest.

Run it once, then commit the file.

snaptest writes snapshots/ExampleTestSnapshots.scala next to the test. It compiles with your tests, so build tools rerun them when it changes.

Change the code, and it fails.

The failure shows the diff and links to the snapshot's line. Rerun with SNAPTEST=always when the change is intended.

ExampleTest.scalamunit
import snaptest.*

class ExampleTest extends munit.FunSuite, MunitSnapshots:
  snapshotTest("one plus one") {
    1 + 1
  }

  test("parser") {
    assertSnapshot(parse("1 + 2"))
  }
snapshots/ExampleTestSnapshots.scala2 recorded
// Generated by snaptest, update with SNAPTEST=always
// format: off
// $COVERAGE-OFF$
package example

/** Snapshots of [[ExampleTest]] */
object ExampleTestSnapshots extends snaptest.Snapshots {
  def entries = Seq(
    "one plus one" -> "2",
    "parser" -> "Sum(Num(1), Num(2))",
  )
}
sbt test1 failed
==> X example.ExampleTest.parser
 9:  test("parser") {
10:    assertSnapshot(parse("1 + 2"))
Snapshot 'parser' does not match, rerun with
SNAPTEST=always to update it
  at snapshots/ExampleTestSnapshots.scala:10
=> Diff (- expected, + obtained)
-Sum(Num(1), Num(2))
+Add(Num(1), Num(2))

Every framework, every platform

Frameworks

Platforms

  • JVMScala 3.9+
  • Scala.js1.x
  • Scala Native0.5

Modes, with SNAPTEST

  • autorecords new, default locally
  • alwaysaccepts every change
  • prunealso removes unchecked
  • nonever writes, default on CI

Why snaptest

Snapshots compile with your tests, failures link to the line, and CI never records them silently.

CI=true sbt test1 failed
==> X example.ParserTest.division
Missing snapshot 'division', run the tests locally or
with SNAPTEST=always to record it
  in snapshots/ParserTestSnapshots.scala
+ example.ParserTest.sum
+ example.ParserTest.product
Tests: 2 passed, 1 failed

Add it to your build

libraryDependencies += "com.github.lolgab" %%% "snaptest-munit" % "0.1.0" % Test
Read the guide