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

# Rstest 实例


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

本页介绍的所有 API 都由 [@rstest/core](https://github.com/web-infra-dev/rstest/tree/main/packages/core) 的 `@rstest/core/api` 入口导出。你可以通过这些 API 创建 Rstest 实例、运行或列出测试、启动监听会话，以及合并 blob 报告。

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

:::warning
目前所有导出都是实验性的，在 Rstest 1.0.0 之前可能发生变化。请暂时锁定 `@rstest/core` 的精确版本来保证 API 稳定性。
:::

## createRstest

`createRstest` 函数用于创建并返回一个 Rstest 实例。`config` 会在创建实例时解析一次，并作为实例的基础配置持续复用。传给实例方法的选项只应用于当前操作，不会修改这份基础配置。

`cwd` 默认为 `process.cwd()`，并作为相对 `config.root` 的解析基准；省略 `config` 时使用空的内联配置，不会从 `cwd` 中发现配置文件。

`createRstest` 会设置 `RSTEST=true`，仅当 `NODE_ENV` 尚未设置时才设置 `NODE_ENV=test`，并且不会恢复这两个环境变量。

### 示例

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

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

const result = await rstest.run();
console.log(result.status);
```

### 加载配置文件

使用主入口的 [`loadConfig`](/zh/api/javascript-api/rstest-core.md#loadconfig) 加载配置文件，再将返回值（`{ content, filePath, dependencies }`）直接作为 `config` 传入。

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

const loaded = await loadConfig();
const rstest = await createRstest({ config: loaded });
```

### CreateRstestOptions

- **类型：**

```ts
interface LoadedRstestConfig {
  content: RstestConfig; // 已加载的配置内容。
  filePath: string | null; // `loadConfig` 返回的配置文件来源路径，没有时为 `null`。
  dependencies?: string[];
}

interface CreateRstestOptions {
  cwd?: string;
  config?: RstestConfig | LoadedRstestConfig;
  configLoader?: 'auto' | 'jiti' | 'native'; // 通过 `projects` 发现的配置文件所使用的 loader；默认为 `auto`。
}

function createRstest(options?: CreateRstestOptions): Promise<RstestInstance>;
```

## rstest.context

`rstest.context` 是一个在创建实例时解析一次的只读对象。你可以在不运行测试的情况下检查解析后的状态，例如用 `rootPath` 确定结果中的 `testPath` 在 workspace 中的位置，用 `projects` 展示或筛选多项目配置，用 `config` 查看生效的配置，也可以用 `version` 做兼容性检查。

### 示例

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

const rstest = await createRstest({
  config: {
    projects: [
      {
        name: 'unit',
        include: ['tests/**/*.test.ts'],
      },
    ],
  },
});

console.log(rstest.context.version);
console.log(rstest.context.rootPath);

for (const project of rstest.context.projects) {
  console.log(project.name, project.rootPath);
}
```

### RstestContext

- **类型：**

```ts
interface ProjectContext {
  name: string; // 项目名称。
  rootPath: string; // 项目的绝对根路径。
  configFilePath?: string; // 与项目关联的配置文件路径（如果有）。
  configFileDependencies?: string[]; // 项目配置依赖，包括父级项目的配置文件。
}

interface RstestContext {
  readonly version: string;
  readonly rootPath: string;
  readonly config: Readonly<NormalizedConfig>;
  readonly projects: readonly ProjectContext[];
}
```

### context.version

当前使用的 `@rstest/core` 版本。

- **类型：** `string`

### context.rootPath

Rstest 实例的绝对根路径。它由 `config.root` 解析得出；当 `config.root` 为相对路径时，以 `cwd` 为基准。

- **类型：** `string`

### context.config

Rstest 实例规范化后的配置。

- **类型：** `Readonly<NormalizedConfig>`

### context.projects

解析后的项目 Context。未显式配置 `projects` 时，该数组包含由 `config.name` 命名的默认项目（默认名称为 `'rstest'`）。

- **类型：** `readonly ProjectContext[]`

## rstest.run

`rstest.run()` 会运行一个测试轮次，并返回 `TestRunResult`。当无效选项、配置或 compiler 错误导致运行无法启动时，Promise 会被拒绝；测试失败仍通过 `status` 报告，不会导致 Promise 被拒绝，具体规则见 [TestRunResult](#testrunresult)。

### 示例

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

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

const result = await rstest.run({
  filters: ['"src/foo.test.ts"'],
});

console.log(result.status);
console.log(result.summary.tests);
```

### RunOptions

`RunOptions` 接受除 `config`、`configLoader`、`root` 和 `trace` 之外的所有 `rstest run` flag。完整 flag 列表见 [CLI 选项](/zh/guide/basic/cli.md#cli-选项)，精确类型见 [`packages/core/src/api/types.ts`](https://github.com/web-infra-dev/rstest/blob/main/packages/core/src/api/types.ts)。

`filters` 仅用于 API；CLI 通过位置参数传入 filter。它默认使用不区分大小写的子字符串匹配。用成对的单引号或双引号包裹 filter，可精确匹配绝对路径或相对于 root 的路径，例如 `filters: ['"src/foo.test.ts"', 'utils']`；带引号和不带引号的 filter 可以混用。省略 `filters` 时选择所有文件；显式传入空数组时不会选择任何文件。

- **类型：**

```ts
interface RunOptions {
  filters?: string[];
  // ……以及其余所有 `rstest run` flag，用于覆盖同名配置项，
  // 例如 `testTimeout`、`coverage`、`reporters`。
}

interface RstestInstance {
  run(options?: RunOptions): Promise<TestRunResult>;
}
```

### TestRunResult

- **类型：**

```ts
type TestRunStatus = 'pass' | 'fail' | 'error';

interface TestRunResult {
  status: TestRunStatus; // 本次运行的整体状态。
  // 其余字段与 `onTestRunEnd` 的 payload 相同，去掉 `getSourcemap`。
}
```

其余字段（`results`、`testResults`、`summary`、`duration`、`snapshotSummary`、`unhandledErrors`、`coverage`、`rerunTestPaths`）的说明见 Reporter API 的 [`onTestRunEnd`](/zh/api/javascript-api/reporter.md#ontestrunend)。

当 `unhandledErrors` 不为空时，`status` 为 `'error'`。当本次运行已完成但退出状态非零时，`status` 为 `'fail'`，例如测试或测试文件失败、覆盖率未达阈值、未设置 `passWithNoTests` 时没有找到测试，或者 `globalSetup` 清理失败。watch 模式下，只要会话里还有失败的测试文件，即使当前轮次全部通过，`status` 仍是 `'fail'`。其他情况下，`status` 为 `'pass'`。

`unhandledErrors` 里的每一项都是普通对象，可以直接序列化为 JSON，不是 Error 实例。

## rstest.watch

`rstest.watch()` 会启动监听会话，并返回用于关闭它的监听器。

### 示例

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

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

const watcher = await rstest.watch({
  onResult(result) {
    console.log(
      result.status,
      result.results.map((file) => file.testPath),
      result.rerunTestPaths,
    );
  },
});

await watcher.close();
```

每一轮结束后都会调用 `onResult`，第一轮也不例外。`results`、`summary` 和 `status` 反映整个 watch 会话的状态，`rerunTestPaths` 是这一轮实际执行的文件。

支持浏览器项目。重跑执行出错会通过 `onResult` 以 `status: 'error'` 交付，不会关闭会话；启动失败（包括浏览器 `globalSetup` 失败）会使 `watch()` 被拒绝。

`onResult` 抛出的错误会被隔离，不会停止监听会话。调用 `watcher.close()` 会释放编译器、worker、文件监听器，并运行待处理的 `globalSetup` 清理函数。该方法是幂等的，重复调用会得到相同结果；清理失败时，它返回的 Promise 会被拒绝。

`rstest.watch()` 会拒绝 `related` 和 `changed`，因为它们会选择固定的文件集合，而监听会话必须能够发现新的关联测试。使用这些选项时请调用 `rstest.run()`。`related: false` 和 `changed: false` 会被忽略。

### WatchOptions

- **类型：**

```ts
interface WatchOptions {
  onResult?: (result: TestRunResult & { rerunTestPaths: string[] }) => void;
}

interface RstestWatcher {
  close(): Promise<void>;
}

interface RstestInstance {
  watch(options?: WatchOptions & RunOptions): Promise<RstestWatcher>;
}
```

## rstest.listTests

`rstest.listTests()` 会收集测试文件和测试声明，但不运行测试主体。

### 示例

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

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

const tests = await rstest.listTests({
  includeSuites: true,
  includeTaskLocation: true,
});

console.log(tests);
```

设置 `filesOnly` 可跳过声明收集。`includeSuites` 会将具名测试套件作为独立条目返回。`RunOptions` 中的 `includeTaskLocation` 会加入源码位置。

每个条目都包含 `testPath` 和 `project`。隐式默认项目的条目使用 `config.name`（默认为 `'rstest'`）。测试声明条目包含自身的 `name`、带测试套件前缀的 `fullName`，以及表示层级的 `parentNames`；文件条目不包含 `name` 和 `fullName`。标记为 `skip` 或 `todo` 的声明也会返回，`runMode` 分别为 `skip` 或 `todo`；可运行声明不提供 `runMode`。

条目按深度优先的声明顺序返回。测试套件紧邻其后代之前，同一文件的所有条目保持连续。

收集失败时，`rstest.listTests()` 返回的 Promise 会以 `ListTestsError` 拒绝，而不是返回部分列表或空列表。

只有显式传给 `rstest.listTests()` 的 `shard` 才会生效，并将结果限制在选定分片。省略 `shard` 时会列出所有文件；实例配置中的 `shard` 仍会像以前一样被丢弃。

### ListOptions

- **类型：**

```ts
interface ListOptions {
  filesOnly?: boolean;
  includeSuites?: boolean;
}

interface ListedTest {
  testPath: string; // 测试文件路径。
  name?: string; // 声明自身的名称；文件条目不包含此字段。
  fullName?: string; // 带测试套件前缀的显示名称；文件条目不包含此字段。
  parentNames?: string[]; // 外层测试套件的名称。
  project: string; // 项目名称。
  location?: TestLocation; // 请求后提供的源码位置。
  runMode?: 'skip' | 'todo'; // 不可运行声明的 skip 或 todo 模式。
  type: 'file' | 'suite' | 'case'; // 条目类型。
}

interface RstestInstance {
  listTests(options?: ListOptions & RunOptions): Promise<ListedTest[]>;
}
```

## rstest.mergeReports

`rstest.mergeReports()` 用于合并 blob 报告，并返回与 `rstest.run()` 相同的结果模型。

当本次操作无法产生运行结果时，Promise 会被拒绝；合并的测试结果中若有测试失败，仍通过 `status` 报告，不会导致 Promise 被拒绝。

### 示例

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

const rstest = await createRstest();

const result = await rstest.mergeReports({
  path: './.rstest-reports',
  cleanup: true,
});

console.log(result.status);
```

`path` 用于选择 blob 报告目录。`cleanup` 会在合并成功后删除已消费的报告。

### MergeReportsOptions

- **类型：**

```ts
interface MergeReportsOptions {
  path?: string;
  cleanup?: boolean;
}

interface RstestInstance {
  mergeReports(options?: MergeReportsOptions): Promise<TestRunResult>;
}
```

## runCLI

`runCLI` 会在当前进程中运行 Rstest 命令行。它会解析 `argv`、设置 `process.exitCode`，并安装 CLI 的 signal handlers。需要 CLI 的进程行为时，请使用 `runCLI`；实例方法不会设置 `process.exitCode` 或安装 signal handlers。

### 示例

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

runCLI({
  argv: [...process.argv.slice(0, 2), 'run', 'src/foo.test.ts', '--update'],
});
```

`argv` 的结构与 Node.js 的 `process.argv` 一致，默认值为 `process.argv`：前两项是 executable 和 script 的路径，会被忽略，命令、filters 和 flags 从索引 2 开始。
这与 Rsbuild 和 Rspress 的 `runCLI` 使用的结构相同。

所有可用的命令和选项，请参阅 [CLI 文档](/zh/guide/basic/cli.md)。

### RunCLIOptions

- **类型：**

```ts
interface RunCLIOptions {
  argv?: string[];
}

function runCLI(options?: RunCLIOptions): void;
```
