test.snapshot
import test.snapshot
testdoes not re-export this module, so it is reached only by importing it directly.
Snapshot testing: recording what a value looked like the first time and failing when it stops looking like that.
it('renders the invoice', @{
expect(render(invoice)).to_match_snapshot()
})
The first run writes the value down. Every run after that compares against what was written and shows a diff when it differs. The file is meant to be committed and reviewed like any other code, because that review is the entire value of the technique: an unexplained change to a snapshot in a pull request is exactly the thing worth noticing.
Where they live
Beside the test file, in a __snapshots__ directory, named after it:
tests/invoice.zu gets tests/__snapshots__/invoice.zu.snap.
The file format
Plain text, indented, one entry per snapshot:
# Zuri snapshot file v1
=== Invoice > renders the invoice 1 ===
{
total: 4200
}
The heading is the test’s full name plus the position of the snapshot
within that test, or the name given to to_match_snapshot('name').
Every body line is indented two spaces, which is what keeps a value that
happens to contain === from being read back as a heading.
Updating
Run with ZURI_UPDATE_SNAPSHOTS=1, or pass { update_snapshots: true }
to run(). Every mismatching snapshot is rewritten and reported as
updated rather than failed, and entries nothing asked for any more are
deleted.
On CI
Writing a brand new snapshot is a pass locally and a failure on CI,
because a snapshot nobody has looked at asserts nothing. CI being set
in the environment is what switches this on; { ci: false } turns it
back off.
Constants
HEADER
test.snapshot.HEADER = '# Zuri snapshot file v1'
BODY_INDENT
test.snapshot.BODY_INDENT = ' '
Functions
path_for()
test.snapshot.path_for(test_file: string) -> string
The .snap file that belongs to a given test file.
Parameters
test_file(string)
Returns string
store_for()
test.snapshot.store_for(test_file: string) -> Store
The store for a test file, read from disk the first time it is asked for and kept afterwards.
Parameters
test_file(string)
Returns Store
updating()
test.snapshot.updating() -> bool
Whether snapshots are being rewritten rather than checked.
Returns bool
set_updating()
test.snapshot.set_updating(on: bool)
Turns snapshot rewriting on or off.
Parameters
on(bool)
strict()
test.snapshot.strict() -> bool
Whether a brand new snapshot counts as a failure, which is what it should be anywhere nobody is going to look at it before it is committed.
Returns bool
set_strict()
test.snapshot.set_strict(on: bool)
Turns the strict, new-snapshots-fail behaviour on or off.
Parameters
on(bool)
check()
test.snapshot.check(test_file: string, key: string, serialized: string)
Compares one value against what the snapshot file holds for key,
writing it down when there is nothing there yet.
Parameters
test_file(string) — the file the test was declared in.key(string)serialized(string) — the value, already rendered byformat.serialize().
Returns — dict: { status, expected, received }, where status is
one of 'match', 'written', 'updated', 'mismatch' or 'missing'.
'missing' is the strict-mode answer to what would otherwise have been
'written'.
recorded_lines()
test.snapshot.recorded_lines(test_file: string, key: string) -> list
The lines of the snapshot at key, as a list, for a reporter that wants
to show what was recorded.
Parameters
test_file(string)key(string)
Returns list
flush()
test.snapshot.flush(prune: ?bool)
Writes every changed store back to disk, and in update mode drops entries the run never asked about.
The runner calls this once, after everything has run. Pruning earlier would delete the snapshots of tests that a filter happened to skip.
Parameters
prune(?bool) — whether to drop unused entries. Defaults to whateverupdating()says.
summary()
test.snapshot.summary()
What happened to snapshots over the whole run.
Returns — dict: matched, written, updated, obsolete (found
and left alone), removed (found and deleted, in update mode), and
missing when strict mode refused to write a new one.
reset()
test.snapshot.reset()
Forgets every loaded store and zeroes the counters, so a second run in the same process starts clean.
Classes
Store
class test.snapshot.Store
Every snapshot recorded for one test file, and the file they live in.
Fields
| Field | Type | Description |
|---|---|---|
path | The .snap file this reads and writes. | |
entries | Snapshot key to recorded text. | |
used | Keys this run actually asked about. | |
dirty | Whether anything has changed since it was read. |
Constructor
test.snapshot.Store(path)
Store.read()
test.snapshot.Store.read()
Reads the file, if it is there. A missing file is not an error: it is what every snapshot’s first run looks like.
Store.save()
test.snapshot.Store.save()
Writes the file back, creating the __snapshots__ directory if it is
not there yet. Does nothing when nothing changed.
An empty store deletes its file rather than leaving an unexplained stub behind.
Store.prune()
test.snapshot.Store.prune() -> int
Drops every entry this run never asked about, and reports how many there were.
Returns int
Store.obsolete()
test.snapshot.Store.obsolete() -> list
Keys the file holds that this run never asked about.
Returns list