# 运行器 (/zh/docs/requests/runner)

运行器逐个发送请求，并告诉你哪些通过了。请求得到应答，而且来自它的[测试标签页和脚本](/docs/requests/scripts-and-tests)的每项检查都成立，才算通过。用它在改动之后试遍整个 API，
用多种输入运行同样的请求，或生成报告。

<Shot id="requests-runner-results" alt="运行整个 Alpenglow 工作区之后的运行器页面：左侧是运行卡片，上方是请求、通过、失败和失败的检查的计数，下面是带状态码和耗时的结果列表" />

## 开始运行 [#start-a-run]

有几种入口：

- 在边栏中右键点按某个文件夹并选取 **运行文件夹**，或右键点按某个请求并选取 **运行请求**。运行会立即开始，并打开运行器页面。
- 在边栏中选中几个文件夹或请求，右键点按它们并选取 **运行 N 个项目**。
- 在命令面板中选取 **运行工作区**。
- 打开运行器页面，设置好运行并选取 **运行**。运行器在边栏的 **测试** 下，也可以选取 **视图 › 运行器**（<kbd>⌥</kbd><kbd>⌘</kbd><kbd>R</kbd>）。

在运行器页面上，**运行** 卡片包含各项设置：

| 设置 | 作用 |
| --- | --- |
| **运行对象** | **整个工作区**、来自 **文件夹** 的一个文件夹，或来自 **一个请求** 的单个请求 |
| **环境** | 请求据以解析的环境，和窗口的其余部分一样 |
| **数据** | **选取 CSV 或 JSON…** 以选取数据文件：每一行对应一次迭代 |
| **迭代** | 整体运行多少次，从 1 到 1000。有数据文件时显示 **每个数据行一次** |
| **延迟** | 请求之间的停顿，从 0 到 60,000 毫秒，步长 100 |
| **重试次数** | 对失败的请求额外尝试的次数，从 0 到 5 |
| **同时运行** | 并排运行多少次迭代，从 1 到 32。迭代多于一次时才显示 |
| **首次失败时停止** | 在第一个失败的请求处结束运行 |

选取 **运行**（<kbd>⌘</kbd><kbd>R</kbd>）开始，选取 **停止**（<kbd>⌘</kbd><kbd>.</kbd>）提前结束运行。当受保护的环境处于启用状态时，按钮显示 **在 Production 中运行…**（带该环境的名称），
运行在发送任何内容之前会先询问。见[受保护的环境](/docs/workspaces/protected-environments)。

## 运行什么，以及顺序 [#what-runs-and-in-which-order]

请求按它们在边栏中的顺序运行：文件夹中的请求从上到下，其中的子文件夹就在原位置。每个请求都与你自己发送时完全一样：同样的变量、认证、脚本和检查。
之后的请求可以使用同一次运行中先前请求的响应，方式是通过[响应值](/docs/requests/dynamic-values)，以及由脚本或[捕获](/docs/requests/captures)设置的变量。例如，运行 Alpenglow 工作区时，
会先发送 **Auth › Sign in**，它捕获 **Orders** 请求所发送的令牌。

每种请求都可以运行：

- HTTP、GraphQL 和 gRPC 请求以及 MCP 调用，与其他请求一样被发送和检查。
- WebSocket、Socket.IO 和 MQTT 请求会连接，发送已为运行开启的已存储消息、事件或发布（MQTT 还会订阅它已启用的过滤器），监听 10 秒，然后关闭。消息日志就是响应，
  所以对 **正文** 的检查也适用于它。要监听不同的时长，请在请求文件的 `[settings]` 中设置 `stream-limit`（单位为秒）。
- 服务器发送事件流会保持 10 秒，然后结束。

请求前脚本可以用 `pm.execution.skipRequest()` 跳过某个请求；结果会显示 **已被请求前脚本跳过**。脚本也可以用 `pm.execution.setNextRequest("Request name")` 跳转，
并用 `pm.execution.setNextRequest(null)` 结束运行。一直往回跳转的运行，会在一次迭代中发送 10,000 次之后被停止。

## 迭代和数据文件 [#iterations-and-data-files]

**迭代** 把所有内容运行这么多次。`{{$uuid}}` 之类的 `{{$…}}` 值每次都是新的，而 `pm.info.iteration` 表明当前是第几次迭代（从 0 开始）。

数据文件让运行由数据驱动：每一行对应一次迭代，每一列是一个变量，请求可以像使用其他变量一样使用它，例如 `{{sku}}`。脚本用 `pm.iterationData.get("sku")` 读取这一行。

- **CSV**：一个包含变量名称的表头行，之后每次迭代一行。引号、成对的引号以及引号内的换行都可以使用。
- **JSON**：对象数组，每次迭代一个对象。

例如，对于发往 `{{baseURL}}/products/{{sku}}`、并检查 `$.sku` 等于 `{{sku}}` 的请求，`skus.csv` 如下：

```csv
sku,quantity
TNT-MH-2P,1
PCK-TIT-30,2
```

或者同样的内容写成 JSON：

```json
[
  { "sku": "TNT-MH-2P", "quantity": 1 },
  { "sku": "PCK-TIT-30", "quantity": 2 }
]
```

**数据** 字段显示文件的名称和它的行数。旁边的叉号会移除该文件。

## 并行迭代 [#parallel-iterations]

迭代多于一次时，**同时运行** 会并排运行多次迭代。同一次迭代内的请求仍保持顺序，而且每次迭代保留它自己脚本所设置的变量，所以一次迭代的令牌绝不会泄漏到另一次迭代中。
当迭代之间互相依赖，或服务器限制调用速度时，请把它保持在 **1（逐个运行）**。

## 重试、延迟和停止 [#retries-delays-and-stopping]

- **重试次数** 会把失败的请求再发送一遍，最多这么多次。请求没有得到应答或某项检查失败，就算失败。结果会显示用了多少次尝试，例如 **3 次尝试**，并保留最后一次。
- **延迟** 在请求之间等待，适用于有速率限制的服务器，例如[演示 API](/docs/tutorials/demo-api)。
- **首次失败时停止** 在某个请求失败（重试之后）时结束运行。摘要随后显示 **因失败而停止**。

## 结果 [#results]

运行期间，列表会逐步填充，并显示正在发送的请求。它上方是各项计数：**请求**、**通过**、**失败**、**失败的检查**，结束时还有 **时长**，以及 **全部通过** 或 **N 项失败**。

每一行显示请求的类型或方法、名称和地址、状态以及耗时。有多次迭代时，`#2` 表明这一行属于哪次迭代。失败的请求下方是失败的检查，每项都带有它的消息，或没有响应的原因。点按某一行可打开该请求。

窗口打开期间，页面会保留最近一次运行。运行的每次交换也都在[历史记录](/docs/workspaces/history)中，并标记为来自运行器。

## 报告 [#reports]

运行之后，在 **结果** 卡片中点按 **存储报告** 可将其存储为文件：

| 格式 | 用途 |
| --- | --- |
| **JUnit XML…** | CI 系统和测试仪表板 |
| **JSON…** | 你自己的工具 |
| **HTML…** | 供阅读或分享的页面 |
| **HAR（所有交换）…** | 运行中的每个请求和响应，供浏览器的开发者工具或其他 HTTP 工具使用 |

JUnit、JSON 和 HTML 报告会遮蔽其中所含的地址、检查值和消息、脚本测试以及日志里的机密。

## 通过命令行运行 [#run-from-the-command-line]

`echolot run` 用同样的设置运行同一个工作区，用于 CI 和终端：迭代、数据文件、延迟、重试、并行迭代以及同样的报告格式。见 [echolot run](/docs/cli/run) 和[在 CI 中运行](/docs/cli/ci)。
