Snapshot Testing is a powerful testing method used to capture and compare serialized representations of component output. When your UI or data structures change, snapshot testing helps you detect these changes promptly.
Rstest provides a simple snapshot testing API that allows you to easily create and use snapshots.
import { test, expect } from '@rstest/core';
test('component snapshot', () => {
const user = { name: 'Alice', age: 25 };
expect(user).toMatchSnapshot();
});For simple values, you can use inline snapshots:
test('inline snapshot', () => {
const message = 'Hello World';
expect(message).toMatchInlineSnapshot('"Hello World"');
});By default, Rstest stores all snapshots for the current test in .snap files with the format exports['testName index'] = ${snapshotContent}.
You can also use the toMatchFileSnapshot method to store snapshots in separate files, which makes them more readable.
test('file snapshot', async () => {
const result = renderHTML(h('div', { class: 'foo' }));
await expect(result).toMatchFileSnapshot('./basic.output.html');
});Rstest provides various snapshot-related configuration options that allow you to customize snapshot behavior.
When test runs encounter snapshot mismatches, Rstest will mark the test as failed and output difference information.
If these changes are expected, you can update snapshots using the command line argument -u or the configuration option update.
npx rstest -uRstest supports adding custom serialization tools for snapshot testing via the addSnapshotSerializer method to handle specific types of data structures.
This is especially useful when your snapshots contain local paths, sensitive data, or other non-serializable objects.
expect.addSnapshotSerializer({
test: (val) => typeof val === 'string' && val.startsWith('secret:'),
print: (val) => '***MASKED***',
});
expect('secret:123').toMatchSnapshot(); // Secret information in snapshot output will be maskedUsing path-serializer can convert local paths to paths relative to the project root, which is very useful in cross-platform testing.
import { createSnapshotSerializer } from 'path-serializer';
expect.addSnapshotSerializer(
createSnapshotSerializer({
root: path.join(__dirname, '..'),
}),
);
test('serialized snapshot', () => {
const filePath = path.join(__dirname, '../data/file.txt');
expect({ filePath }).toMatchSnapshot();
// before: "/Users/aaa/Desktop/projects/rstest/examples/node/data/file.txt"
// after: "<ROOT>/examples/node/data/file.txt"
});Snapshot files are saved with the .snap extension by default, in the __snapshots__ folder in the same directory as the test file.
src/
├── components/
│ ├── button.test.ts
│ └── __snapshots__/
│ └── button.test.ts.snapYou can customize snapshot file paths through the resolveSnapshotPath configuration option.
import { defineConfig } from '@rstest/core';
export default defineConfig({
resolveSnapshotPath: (testPath, snapExtension) => {
// Store snapshots at the same level as test files
return `${testPath}${snapExtension}`;
},
});Snapshots should be readable and easy to understand, avoiding containing too much irrelevant information:
// ✅ Good practice - only include necessary information
test('user info snapshot', () => {
const userInfo = {
name: 'Alice',
role: 'admin',
permissions: ['read', 'write'],
};
expect(userInfo).toMatchSnapshot();
});
// ❌ Avoid - includes too much irrelevant information
test('complete user object snapshot', () => {
const fullUser = {
...user,
lastLogin: new Date(),
sessionId: 'abc123',
userAgent: 'Mozilla/5.0...',
};
expect(fullUser).toMatchSnapshot();
});When using multiple snapshots, provide descriptive names for each snapshot:
test('component state changes', () => {
const component = new MyComponent();
expect(component.initialState).toMatchSnapshot('initial state');
component.update();
expect(component.updatedState).toMatchSnapshot('updated state');
});As code evolves, snapshots may become outdated. Regularly reviewing and updating snapshots is necessary: