> For AI agents: the complete documentation index is available at /zh/llms.txt, the full documentation bundle is available at /zh/llms-full.txt.

# Metadata


[Added in v0.11.1](https://github.com/web-infra-dev/rstest/releases/tag/v0.11.1)

Metadata 允许你为测试、套件和文件结果附加可 JSON 序列化的数据。内置 Reporter 默认不会打印这些数据；它主要面向自定义 Reporter、programmatic API、CI 集成、仪表盘、归属关系映射，以及 mutation testing runner 等外部工具。

## 为测试添加 metadata

在测试选项对象中传入 `meta`，即可设置测试用例的初始 metadata。

```ts
import { expect, test } from '@rstest/core';

test(
  'charges credit card',
  { meta: { owner: 'payments', feature: 'checkout' } },
  () => {
    expect(1 + 1).toBe(2);
  },
);
```

Reporter hooks 和 programmatic API 可以从 `TestResult.meta` 读取这些声明的 metadata。你可以在测试声明时定义 metadata，也可以在测试运行过程中通过 `task.meta` 修改或添加字段：

```ts
test('records runtime data', ({ task }) => {
  task.meta = {
    durationBucket: 'fast',
  };
});
```

## 为套件添加 metadata

当多个测试共享相同字段时，可以把 `meta` 传给 `describe`。

```ts
import { describe, expect, test } from '@rstest/core';

describe(
  'checkout',
  { meta: { owner: 'payments', layer: 'integration' } },
  () => {
    test('creates an order', ({ task }) => {
      // task.meta 是 { owner: 'payments', layer: 'integration' }
      expect(task.meta.owner).toBe('payments');
    });

    test(
      'uses coupon',
      { meta: { layer: 'e2e', caseId: 'PAY-456' } },
      ({ task }) => {
        // task.meta 是 { owner: 'payments', layer: 'e2e', caseId: 'PAY-456' }
        expect(task.meta.layer).toBe('e2e');
      },
    );
  },
);
```

套件 metadata 会被子套件和测试继承。子级顶层字段会覆盖父级同名字段，因此单个测试只需要覆盖不同的字段。

## 从 Hook 添加文件和套件 metadata

生命周期 Hook 可以通过 `ctx.meta` 获取 metadata 对象。

```ts
import { afterAll, describe, test } from '@rstest/core';

afterAll((ctx) => {
  ctx.meta.fileTag = 'ci-shard-1';
});

describe('checkout', { meta: { owner: 'payments' } }, () => {
  afterAll((ctx) => {
    ctx.meta.suiteDurationBucket = 'slow';
  });

  test('creates an order', () => {
    // ...
  });
});
```

文件级 Hook 会写入 `TestFileResult.meta`。`describe` 内部的 Hook 会写入对应套件结果的 `meta`，并通过 `onTestSuiteResult` 传给自定义 Reporter。

## 在自定义 Reporter 中读取 metadata

自定义 Reporter 可以从 case、suite 和 file 结果中读取 metadata。

```ts
import type { Reporter } from '@rstest/core';

const metadataReporter: Reporter = {
  onTestCaseStart(test) {
    // 经过套件继承和测试级覆盖后的初始 metadata。
    console.log('case start', test.name, test.meta);
  },

  onTestCaseResult(result) {
    // 测试执行和 hooks 运行后的最终 metadata。
    console.log('case result', result.name, result.meta);
  },

  onTestSuiteResult(result) {
    console.log('suite result', result.name, result.meta);
  },

  onTestFileResult(result) {
    console.log('file result', result.testPath, result.meta);
  },
};
```

如果需要测试运行前声明的 metadata，使用 `onTestCaseStart`。如果需要运行时产生的最终 metadata，使用 `onTestCaseResult`、`onTestSuiteResult` 或 `onTestFileResult`。

## 从 programmatic API 读取 metadata

`rstest.run()` 返回的结果中包含与 Reporter 相同的结果 metadata。

```ts
import { createRstest } from '@rstest/core/api';

const rstest = await createRstest({
  cwd: process.cwd(),
  config: {
    include: ['src/**/*.test.ts'],
  },
});
const result = await rstest.run();

for (const file of result.results) {
  console.log(file.testPath, file.meta);

  for (const test of file.results) {
    console.log(test.name, test.meta);
  }
}
```

当外部工具掌控 Node.js 进程，并希望运行 Rstest 后直接消费结构化结果，而不是解析终端输出时，这种方式很有用。

## Metadata 值类型

Metadata 必须可 JSON 序列化。`TaskMeta` 和 `TaskMetaValue` 从 `@rstest/core` 导出：

```ts
type TaskMeta = Record<string, TaskMetaValue>;

type TaskMetaValue =
  | string
  | number
  | boolean
  | null
  | TaskMetaValue[]
  | { [key: string]: TaskMetaValue };
```

不要在 metadata 中存储函数、类实例、symbol、`BigInt` 或大型对象。建议使用小型字符串、数字、布尔值、数组和普通对象。

## 继承和隔离规则

Rstest 在收集和上报 metadata 时遵循以下规则：

- 套件 metadata 会被子套件和测试继承。
- 子级顶层字段会覆盖父级顶层字段。
- 继承的 metadata 会为每个子级复制，因此运行时修改不会泄漏到兄弟测试或兄弟套件。
- 嵌套对象和数组会在继承时复制。
- 在测试中修改 `task.meta` 会影响该测试最终的 `TestResult.meta`。
- 在文件级 Hook 中修改 `ctx.meta` 会影响 `TestFileResult.meta`；在套件 Hook 中修改它会影响该套件结果的 `meta`。

## Skipped 和 todo 测试

跳过和 todo 测试仍然会保留声明的 metadata。

```ts
import { test } from '@rstest/core';

test.skip(
  'blocked by upstream',
  { meta: { owner: 'payments', reason: 'upstream' } },
  () => {
    // 这个函数体不会运行。
  },
);

test.todo('add refund coverage', {
  meta: { owner: 'payments', tracking: 'PAY-789' },
});
```

因为这些测试不会执行，所以无法进行运行时修改，但 Reporter 仍然可以从 `onTestCaseStart` 和 `onTestCaseResult` 读取它们声明的 metadata。

## 什么时候使用 metadata

常见使用场景包括：

- 归属字段，例如 `owner`、`team` 或 `feature`。
- 测试管理系统链接，例如 `caseId` 或 `tracking`。
- CI 仪表盘字段，例如 shard、environment 或 risk level。
- 自定义 Reporter 输出，例如 JSON、Markdown 或内部仪表盘。
- 需要把测试结果映射到源码分析的外部工具，例如 mutation testing runner。

## 最佳实践

- 保持值小且可 JSON 序列化。
- 优先使用稳定的字段名，例如 `owner`、`feature`、`caseId` 和 `tracking`。
- 将共享字段放在 `describe` 上，只在单个测试上覆盖例外。
- 只有当 metadata 依赖运行时发现的信息时，才在运行时写入。
- 使用自定义 Reporter 或 programmatic API 消费 metadata；内置 Reporter 更适合人类可读的测试输出。

## 相关 API

- [`test` options](/zh/api/runtime-api/test-api/test.md#testoptions)
- [`describe` options](/zh/api/runtime-api/test-api/describe.md#testoptions)
- [生命周期 Hooks](/zh/api/runtime-api/test-api/hooks.md)
- [Reporter](/zh/api/javascript-api/reporter.md)
- [Rstest 类型](/zh/api/javascript-api/types.md)
