close
  • 简体中文
  • 快照测试

    快照测试(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 来更新快照。

    CLI
    rstest.config.ts
    npx rstest -u

    序列化输出

    Rstest 支持通过 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('更新后状态');
    });

    定期审查快照

    随着代码的演进,快照可能会过时。定期审查和更新快照是必要的:

    • 在重构 UI 时更新相关快照
    • 删除不再需要的快照
    • 合理使用内联快照进行小范围测试