快照测试
toMatchSnapshot — 基础用法
import { render } from '@testing-library/react';
import { Button } from './Button';
test('Button 渲染正确', () => {
const { asFragment } = render(<Button label="点击我" />);
expect(asFragment()).toMatchSnapshot();
});
// 对象/值的快照
test('转换用户数据', () => {
const result = transformUser({ id: 1, name: '张三', role: 'admin' });
expect(result).toMatchSnapshot();
});
// 快照文件保存在 __snapshots__/Button.test.tsx.snap
内联快照
// 首次运行时 Jest 自动写入快照值
test('格式化货币', () => {
expect(formatCurrency(1234.5, 'CNY')).toMatchInlineSnapshot(`"¥1,234.50"`);
});
test('错误对象结构', () => {
const err = createError('NOT_FOUND', '用户未找到');
expect(err).toMatchInlineSnapshot(`
Object {
"code": "NOT_FOUND",
"message": "用户未找到",
"timestamp": Any<String>,
}
`);
});
// 优点:快照与测试代码在同一文件,PR 审查更方便
更新快照
# 更新所有过期/变更的快照
npx jest --updateSnapshot
npx jest -u
# 更新特定测试文件的快照
npx jest Button.test.tsx --updateSnapshot
# 交互式更新模式
npx jest --watch
# 然后按 'u' 键更新失败的快照
# CI 环境:有新快照或变更时失败
npx jest --ci
自定义快照序列化器
// 为特殊对象类型添加自定义序列化器
expect.addSnapshotSerializer({
test: (val) => val && val.__type === 'Color',
print: (val) => `Color(${val.r}, ${val.g}, ${val.b})`,
});
test('颜色对象', () => {
expect({ __type: 'Color', r: 255, g: 0, b: 128 })
.toMatchInlineSnapshot(`Color(255, 0, 128)`);
});
// jest.config.js — 全局序列化器
module.exports = {
snapshotSerializers: [
'enzyme-to-json/serializer',
'./src/test-utils/mySerializer.js',
],
};
异步组件与动态值
import { render, screen, waitFor } from '@testing-library/react';
test('异步数据加载到组件', async () => {
const { asFragment } = render(<UserProfile userId="1" />);
await waitFor(() => screen.getByText('张三'));
expect(asFragment()).toMatchSnapshot();
});
// 使用属性匹配器屏蔽动态值
test('API 响应结构', () => {
const response = { id: 123, createdAt: new Date().toISOString(), name: '李四' };
expect(response).toMatchSnapshot({
id: expect.any(Number),
createdAt: expect.any(String),
});
});
快照测试最佳实践
| 实践 | 原因 |
|---|---|
| 将 .snap 文件提交到 git | 快照是测试套件的一部分 |
| 在 PR 中审查快照差异 | 捕获意外的 UI 回归 |
| 保持快照小而聚焦 | 大快照难以审查 |
| 简单值用内联快照 | 上下文保留在同一文件中 |
| ID/时间戳使用属性匹配器 | 避免动态数据导致测试不稳定 |