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

# 编写与设计 YAML 用例

## 一个典型的 YAML 文件

一份 YAML 文件可以包含多个测试用例，以及用例执行前后的准备和清理步骤。

下面以商城搜索为例：每次运行用例前先打开首页，再搜索商品并验证结果（实际使用时请将网址和商品名称替换为你的业务内容）：

```yaml filename=cases/search.yaml
beforeEach:
  - gotoUrl: https://your-shop.example

cases:
  - name: 搜索商品
    steps:
      - aiAct: 在搜索框输入“马克杯”，点击搜索按钮
      - aiAssert: 搜索结果中包含马克杯
```

在 Midscene Test 中，我们遵循以下术语定义：

- **Workflow Document（工作流文档）**：整份 YAML 文件。
- **Case（用例）**：`cases` 列表下的单条测试用例。
- **Step（步骤）**：`steps` 列表下（或生命周期钩子内）的单次 Node 调用，每个步骤只调用一个 Node。
- **Hook（生命周期钩子）**：在用例执行前后运行的准备与清理步骤（如 `beforeAll`、`beforeEach`、`afterEach`、`afterAll`）。

上面的示例使用了字符串简写形式：`gotoUrl` 后的字符串会作为 `url` 参数传入，`aiAct` 和 `aiAssert` 后的字符串会作为 `prompt` 参数传入。如果需要传入更多参数，可以使用下文介绍的对象写法。某个 Node 是否支持字符串简写，以项目的 Node Spec 为准。

## 用例设计约定 {#用例设计约定}

Midscene Test 采用顺序执行与显式状态共享的设计，使每个 Node 的输入参数、执行上下文和依赖关系保持清晰：

- **按声明顺序线性执行**：YAML 仅描述线性的测试步骤，不引入 DAG、条件分支或循环语法。如果遇到需要复杂动态编排的场景，建议在外部通过代码生成确定的线性 YAML 步骤序列，再交由 Midscene Test 执行。
- **每一个语句（步骤）都是无状态且独立的**：每一个步骤都是一次完全独立的 Node 语句调用，步骤之间不存在隐式的状态和数据传递（不支持跨步骤输出引用语法）。每次调用仅依赖当前步骤显式声明的输入参数以及项目级别的 `context`。如果你需要在步骤之间显式共享数据，请通过自定义 Node 读写执行项目共享的 `context`。

基于这一约定，每个 AI 步骤的自然语言指令也应当独立完整。例如，下面的断言使用“上一步的图标”来指代检查目标，依赖前一步的指令文本，属于错误写法：

```yaml
steps:
  - aiAct: 点击商品详情页的收藏星标，将其点亮
  # ❌ 错误：断言不会继承前一步的指令上下文，无法通过“上一步的图标”明确识别目标。
  - aiAssert: 上一步的图标已经点亮
```

正确的做法是在断言中直接写明检查对象与预期状态，使该步骤本身就能表达完整的检查条件：

```yaml
steps:
  - aiAct: 点击商品详情页的收藏星标，将其点亮
  - aiAssert: 商品详情页的收藏星标处于点亮状态
```

简言之，虽然界面本身会保留前一步操作后的画面，但每条自然语言指令都应明确写出目标元素，避免使用“上一步”“刚才那个”等指代词。

## 使用 Node

Midscene Test 将每一种可在 YAML 中调用的能力称为 **Node（节点）**。

Node 既可以在用例的 `steps` 中调用，也可以在 `beforeEach`、`afterEach` 等[生命周期钩子](/zh/midscene-test/execution.md#yaml-中的生命周期钩子)中调用，语法完全一致。

Midscene Test 注入了 Midscene GUI Agent 上的能力：`aiAct` 根据自然语言操作界面，`aiAssert` 验证预期结果，`aiWaitFor` 等待界面出现指定状态；同时提供了框架内置的 `wait` 等待指定时长。

在 GUI Agent 的能力之外，Midscene 还为各个平台预置了专用操作。例如：

- **Web**：`gotoUrl` 打开网页、`setCookies` 设置 Cookie、`setViewportSize` 调整浏览器视口大小。
- **Android**：`launch` 启动应用、`back` 返回上一页、`home` 返回主屏幕、`runAdbShell` 执行 ADB 命令。

使用脚手架创建项目时，模板会根据所选平台显式注册对应的官方 Node。如果从零手写 `midscene.config.ts`，框架不会默认注册任何 Node，需要手动引入并配置。各平台支持的官方预置 Node 列表与参数说明，请参阅[各平台预置 Node](/zh/midscene-test/builtin-nodes.md)。此外，你也可以按需扩展自定义能力（例如调用业务接口准备测试订单），详见[注册自定义业务 Node](/zh/midscene-test/extend.md#注册自定义业务-node)。

### 常用 Node

日常编写用例时，主要使用 `aiAct` 描述操作，使用 `aiAssert` 验证结果：

| Node       | 用途                       | 编写方式                            |
| ---------- | ------------------------ | ------------------------------- |
| `aiAct`    | 根据自然语言完成界面操作，可包含连续的多个动作。 | 描述要执行的操作，例如“搜索马克杯，将第一个商品加入购物车”。 |
| `aiAssert` | 检查界面是否符合预期；若不符合，则该步骤失败。  | 描述界面上可观察到的预期结果，例如“购物车中有一件马克杯”。  |

界面操作执行完成并不代表测试通过，应在关键节点使用 `aiAssert` 明确验证预期结果。如果需要自定义断言失败时的报错提示，可以将参数展开为对象形式：

```yaml
steps:
  - aiAct: 搜索马克杯，将第一个商品加入购物车
  - aiAssert:
      prompt: 购物车中有一件马克杯
      message: 加购后购物车内容不符合预期
```

若需要为后续 AI 操作补充业务背景知识或通用规则（如测试账号约定或弹窗处理），也可以调用 `setAIContext` 设置或清除上下文。

### 其他 Node 的调用模式

平台内置操作与自定义业务 Node 采用相同的调用结构：**在 Node 名称下方以键值对形式填写参数**。下面展示含多个参数与无参数两种常见写法：

```yaml
steps:
  - setViewportSize:
      width: 1440
      height: 900
  - clearCookies: {}
```

其中 `setViewportSize` 接收一个参数对象，而 `clearCookies` 不需要参数，传入空对象 `{}` 即可。这两个 Node 均由 Web 平台预设提供；移动端、桌面端及团队自定义 Node 的可用列表与参数结构，请查阅当前项目的 Node Spec。

例如，当团队在项目中实现并注册了用于创建测试订单的自定义 Node `order.create` 后，即可在 YAML 中这样调用：

```yaml
steps:
  - order.create:
      sku: midscene-mug
      quantity: 2
```

参数值同样支持嵌套对象或数组。例如向 `aiAct` 传入参考图片时，文本和图片列表共同构成 `prompt` 对象，而 `options` 作为同级字段单独配置：

```yaml
steps:
  - aiAct:
      prompt:
        prompt: 按照参考图完成设置
        images:
          - name: 目标状态
            url: ./fixtures/target.png
      options:
        deepLocate: true
```

### 生成当前项目的 Node Spec {#生成当前项目的-node-说明书}

不同平台、执行项目（Project）和依赖版本实际注册的 Node 可能有所不同。编写用例时，请勿仅凭其他项目的示例推测当前项目的可用能力。

运行 `midscene-test nodes` 可以根据项目最终生效的注册结果生成 Node Spec（节点规格说明）——它列出可用 Node、输入参数结构（由 Zod 转换为 JSON Schema）、字符串简写规则及匹配的用例文件路径，是人和 AI Agent 编写 YAML 用例的依据。

查看生成的 Node Spec 示例片段

````markdown filename=midscene-node-spec.md
# Midscene Test Node Spec

## Available Nodes

| Node | Description |
| --- | --- |
| aiAct | Perform interface operations from natural language. |
| aiAssert | Check whether the interface matches an expectation. |
| gotoUrl | Navigate the current browser Page to an absolute HTTP(S) URL or a relative path. |

## Node Details

### `gotoUrl`

Navigate the current browser Page to an absolute HTTP(S) URL or a path relative to testRunner.baseURL or the current page URL.

**String shorthand:** Maps to `{ url: value }`.

#### Input Schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "url": {
      "type": "string",
      "description": "Navigation URL target."
    }
  },
  "required": [
    "url"
  ]
}
```
````

脚手架在安装依赖后会自动生成 Node Spec。当你修改了 `agentClass`、顶层 `nodes`、`projects[].nodes`、Node 的参数 Schema，或升级了 Midscene 相关依赖包后，都应重新运行以下命令更新 Spec：

```bash
pnpm run nodes
```

你也可以直接调用 CLI 命令，并按需指定测试目录、配置文件或执行项目：

```bash
pnpm exec midscene-test nodes ./e2e --config ../config/midscene.config.ts
pnpm exec midscene-test nodes --project android-smoke
```

生成命令仅加载项目配置与 Node 定义，不会执行 `setup`、测试用例或 `getAgent`，因此无需启动浏览器、连接设备或调用大模型。Node Spec 属于自动生成的文件，请勿手动编辑；配置变更后重新运行生成命令即可。

## 控制参数与高级配置

### 设置超时和错误处理

步骤中的 `$` 字段用于配置框架级的步骤控制参数（如超时时间与容错策略）。这些参数由 Midscene Test 处理，不会传递给 Node 的 `input`。

```yaml
steps:
  - order.create:
      sku: midscene-mug
      quantity: 1
      $:
        timeout: 30000
        continue-on-error: true
```

`$` 支持以下两个字段：

- `timeout`：当前步骤的超时时间，单位为毫秒。
- `continue-on-error`：设为 `true` 时，若当前步骤执行失败，允许继续执行当前阶段的后续步骤（默认值为 `false`）。但如果底层操作未能正常退出或资源清理出现异常，执行仍会中止。

注意，`continue-on-error` 仅控制当前阶段是否继续往下走；只要用例中有任何一个步骤失败，该用例的最终状态仍为 `failed`。

步骤超时并不意味着底层的异步操作已立即终止。在进入当前阶段的下一个步骤前，框架会等待已超时的操作彻底退出（默认最多等待 30 秒，独立计时，不占用下一步的超时时间）；若等待超时后仍未退出，则会停止执行后续步骤。底层操作退出后虽然可以继续使用 Agent，但页面或业务状态可能已处于中间态，仍需在后续步骤中确认或重置，详见[超时与取消机制](/zh/midscene-test/execution.md#超时与取消机制)。

### 使用项目变量与环境变量

Midscene Test 会在步骤执行前递归解析 Node 输入参数中的变量占位符：

```yaml
steps:
  - launch:
      uri: ${appUri}
  - api.createOrder:
      baseURL: ${{TEST_API_BASE_URL}}
      payload:
        count: ${orderCount}
```

变量解析遵循以下规则：

- `${name}`：读取当前执行项目的 `variables` 配置。若整个字段值仅由 `${name}` 构成（未与其他文本拼接），则会保留变量的原始 JSON 类型（如数字、布尔值、对象或数组）；对象或数组变量只能作为完整字段值使用，不支持嵌入到字符串中拼接。
- `${{ENV_NAME}}`：读取环境变量，解析结果始终为字符串。
- 引用未定义的变量会在用例收集阶段直接报错。变量替换仅作用于 Node 的输入参数，不作用于 `$` 控制字段。

关于用例中步骤执行的无状态设计，请参阅[用例设计约定](#用例设计约定)。

### 使用 tags 筛选 Case

你可以为用例添加 `tags` 标签，以便按场景或平台筛选运行：

```yaml
cases:
  - name: Android 冒烟下单
    tags: [smoke, android]
    steps:
      - aiAct: 完成下单
```

工程维护者可以在各个执行项目（Project）中配置 `tags.include` 与 `tags.exclude`：排除规则（`exclude`）始终优先；当包含规则（`include`）非空时，用例只需命中其中任意一个标签即会被选中执行。

## 下一步

- [生命周期与并发](/zh/midscene-test/execution.md)：了解 `beforeAll`、`beforeEach` 等钩子的完整执行机制、重试规则与并发策略。
- [开发自定义 Node](/zh/midscene-test/extend.md)：封装可复用的业务操作。
- [运行测试](/zh/midscene-test/running.md)：执行用例、获取诊断输出与报告链接。
