Snapshot
Snapshot tests are a very useful tool whenever you want to make sure the output of your functions does not change unexpectedly.
When using snapshot, Vitest will take a snapshot of the given value, then compares it to a reference snapshot file stored alongside the test. The test will fail if the two snapshots do not match: either the change is unexpected, or the reference snapshot needs to be updated to the new version of the result.
Use Snapshots
To snapshot a value, you can use the toMatchSnapshot()
from expect()
API:
import { expect, it } from 'vitest'
it('toUpperCase', () => {
const result = toUpperCase('foobar')
expect(result).toMatchSnapshot()
})
import { expect, it } from 'vitest'
it('toUpperCase', () => {
const result = toUpperCase('foobar')
expect(result).toMatchSnapshot()
})
The first time this test is run, Vitest creates a snapshot file that looks like this:
// Vitest Snapshot v1
exports['toUpperCase 1'] = '"FOOBAR"'
// Vitest Snapshot v1
exports['toUpperCase 1'] = '"FOOBAR"'
The snapshot artifact should be committed alongside code changes, and reviewed as part of your code review process. On subsequent test runs, Vitest will compare the rendered output with the previous snapshot. If they match, the test will pass. If they don't match, either the test runner found a bug in your code that should be fixed, or the implementation has changed and the snapshot needs to be updated.
Inline Snapshots
Similarly, you can use the toMatchInlineSnapshot()
to store the snapshot inline within the test file.
import { expect, it } from 'vitest'
it('toUpperCase', () => {
const result = toUpperCase('foobar')
expect(result).toMatchInlineSnapshot()
})
import { expect, it } from 'vitest'
it('toUpperCase', () => {
const result = toUpperCase('foobar')
expect(result).toMatchInlineSnapshot()
})
Instead of creating a snapshot file, Vitest will modify the test file directory to update the snapshot as a string:
import { expect, it } from 'vitest'
it('toUpperCase', () => {
const result = toUpperCase('foobar')
expect(result).toMatchInlineSnapshot('"FOOBAR"')
})
import { expect, it } from 'vitest'
it('toUpperCase', () => {
const result = toUpperCase('foobar')
expect(result).toMatchInlineSnapshot('"FOOBAR"')
})
This allows you to see the expected output directly without jumping across different files.
Updating Snapshots
When the received value doesn't match the snapshot, the test fails and shows you the difference between them. When the snapshot change is expected, you may want to update the snapshot from the current state.
In watch mode, you can press the u
key in the terminal to update the failed snapshot directly.
Or you can use the --update
or -u
flag in the CLI to make Vitest update snapshots.
vitest -u
vitest -u
Image Snapshots
It's also possible to snapshot images using jest-image-snapshot
.
npm i -D jest-image-snapshot
npm i -D jest-image-snapshot
test('image snapshot', () => {
expect(readFileSync('./test/stubs/input-image.png'))
.toMatchImageSnapshot()
})
test('image snapshot', () => {
expect(readFileSync('./test/stubs/input-image.png'))
.toMatchImageSnapshot()
})
You can learn more in the examples/image-snapshot
example.
Custom Serializer
You can add your own logic to alter how your snapshots are serialized. Like Jest, Vitest has default serializers for built-in JavaScript types, HTML elements, ImmutableJS and for React elements.
Example serializer module:
expect.addSnapshotSerializer({
serialize(val, config, indentation, depth, refs, printer) {
// `printer` is a function that serializes a value using existing plugins.
return `Pretty foo: ${printer(val.foo)}`
},
test(val) {
return val && Object.prototype.hasOwnProperty.call(val, 'foo')
},
})
expect.addSnapshotSerializer({
serialize(val, config, indentation, depth, refs, printer) {
// `printer` is a function that serializes a value using existing plugins.
return `Pretty foo: ${printer(val.foo)}`
},
test(val) {
return val && Object.prototype.hasOwnProperty.call(val, 'foo')
},
})
After adding a test like this:
test('foo snapshot test', () => {
const bar = {
foo: {
x: 1,
y: 2,
},
}
expect(bar).toMatchSnapshot()
})
test('foo snapshot test', () => {
const bar = {
foo: {
x: 1,
y: 2,
},
}
expect(bar).toMatchSnapshot()
})
You will get the following snapshot:
Pretty foo: Object {
"x": 1,
"y": 2,
}
Pretty foo: Object {
"x": 1,
"y": 2,
}
We are using Jest's pretty-format
for serializing snapshots. You can read more about it here: pretty-format.
Difference from Jest
Vitest provides an almost compatible Snapshot feature with Jest's with a few exceptions:
1. Comment header in the snapshot file is different
- // Jest Snapshot v1
+ // Vitest Snapshot v1
- // Jest Snapshot v1
+ // Vitest Snapshot v1
This does not really affect the functionality but might affect your commit diff when migrating from Jest.
2. printBasicPrototype
is default to false
Both Jest and Vitest's snapshots are powered by pretty-format
. In Vitest we set printBasicPrototype
default to false
to provide a cleaner snapshot output, while in Jest it's true
by default.
import { expect, test } from 'vitest'
test('snapshot', () => {
const bar = [
{
foo: 'bar',
},
]
// in Jest
expect(bar).toMatchInlineSnapshot(`
Array [
Object {
"foo": "bar",
},
]
`)
// in Vitest
expect(bar).toMatchInlineSnapshot(`
[
{
"foo": "bar",
},
]
`)
})
import { expect, test } from 'vitest'
test('snapshot', () => {
const bar = [
{
foo: 'bar',
},
]
// in Jest
expect(bar).toMatchInlineSnapshot(`
Array [
Object {
"foo": "bar",
},
]
`)
// in Vitest
expect(bar).toMatchInlineSnapshot(`
[
{
"foo": "bar",
},
]
`)
})
We believe this is a more reasonable default for readability and overall DX. If you still prefer Jest's behavior, you can change your config:
// vitest.config.js
export default defineConfig({
test: {
snapshotFormat: {
printBasicPrototype: true
}
}
})
// vitest.config.js
export default defineConfig({
test: {
snapshotFormat: {
printBasicPrototype: true
}
}
})