快照测试(Snapshot Testing)是一种强大的测试方法,用于捕获和比较组件输出的序列化表示。当你的 UI 或数据结构发生变化时,快照测试能够帮助你及时发现这些变更。
Rstest 提供了简单的快照测试 API,让你可以轻松创建和使用快照。
import { test, expect } from '@rstest/core';
test('组件快照', () => {
const user = { name: 'Alice', age: 25 };
expect(user).toMatchSnapshot();
});对于简单的值,可以使用内联快照:
test('内联快照', () => {
const message = 'Hello World';
expect(message).toMatchInlineSnapshot('"Hello World"');
});默认情况下,Rstest 会将当前测试的所有快照以 exports['测试名称 索引'] = ${快照内容} 的格式存储在 .snap 文件中。
你也可以使用 toMatchFileSnapshot 方法将快照存储在单独的文件中,这将使它们更具可读性。
test('文件快照', async () => {
const result = renderHTML(h('div', { class: 'foo' }));
await expect(result).toMatchFileSnapshot('./basic.output.html');
});Rstest 提供了多种快照相关的配置选项,让你可以自定义快照的行为。
当测试运行出现快照不匹配时,Rstest 会将测试标记为失败并输出差异信息。
如果这些变更是预期内的,你可以通过命令行参数 -u 或配置选项 update 来更新快照。
npx rstest -uRstest 支持通过 addSnapshotSerializer 方法为快照测试添加自定义序列化工具,以处理特定类型的数据结构。
这尤其有用当你的快照中包含本地路径、敏感数据或其他非可序列化的对象。
expect.addSnapshotSerializer({
test: (val) => typeof val === 'string' && val.startsWith('secret:'),
print: (val) => '***MASKED***',
});
expect('secret:123').toMatchSnapshot(); // 快照输出的 secret 信息会被掩码使用 path-serializer 可以将本地路径转换为相对于项目根目录的路径,这在跨平台测试中非常有用。
import { createSnapshotSerializer } from 'path-serializer';
expect.addSnapshotSerializer(
createSnapshotSerializer({
root: path.join(__dirname, '..'),
}),
);
test('序列化快照', () => {
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"
});快照文件默认以 .snap 为扩展名,保存在与测试文件相同的目录下的 __snapshots__ 文件夹中。
src/
├── components/
│ ├── button.test.ts
│ └── __snapshots__/
│ └── button.test.ts.snap你可以通过 resolveSnapshotPath 配置选项自定义快照文件路径。
import { defineConfig } from '@rstest/core';
export default defineConfig({
resolveSnapshotPath: (testPath, snapExtension) => {
// 将快照存储在测试文件同级
return `${testPath}${snapExtension}`;
},
});快照应该是可读且易于理解的,避免包含过多无关信息:
// ✅ 好的做法 - 只包含必要信息
test('用户信息快照', () => {
const userInfo = {
name: 'Alice',
role: 'admin',
permissions: ['read', 'write'],
};
expect(userInfo).toMatchSnapshot();
});
// ❌ 避免 - 包含过多无关信息
test('完整用户对象快照', () => {
const fullUser = {
...user,
lastLogin: new Date(),
sessionId: 'abc123',
userAgent: 'Mozilla/5.0...',
};
expect(fullUser).toMatchSnapshot();
});当使用多个快照时,为每个快照提供描述性的名称:
test('组件状态变化', () => {
const component = new MyComponent();
expect(component.initialState).toMatchSnapshot('初始状态');
component.update();
expect(component.updatedState).toMatchSnapshot('更新后状态');
});随着代码的演进,快照可能会过时。定期审查和更新快照是必要的: