bruno-api-testing

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Bruno API Testing

Bruno API测试

Create and run API test collections using Bruno — a Git-first, offline-only API client that stores collections as plain files.
使用Bruno创建并运行API测试集合——Bruno是一款优先支持Git、仅离线使用的API客户端,以纯文件形式存储集合。

Format Selection

格式选择

Bruno supports two file formats. Determine which to use:
  • YAML (OpenCollection) — Default since Bruno v3.1. Uses
    .yml
    files and
    opencollection.yml
    root. Preferred for new projects.
  • Bru (Legacy) — Uses
    .bru
    files and
    bruno.json
    root. Use only for existing Bru-format collections.
Detect format by checking the collection root:
opencollection.yml
→ YAML,
bruno.json
→ Bru.
For YAML format syntax details, see references/yaml-syntax.md. For Bru format syntax details, see references/bru-syntax.md.
Bruno支持两种文件格式。请确定要使用的格式:
  • YAML(OpenCollection) —— Bruno v3.1起的默认格式。使用.yml文件和opencollection.yml根文件。推荐用于新项目。
  • Bru(旧版) —— 使用.bru文件和bruno.json根文件。仅用于现有Bru格式的集合。
通过检查集合根文件判断格式:存在opencollection.yml则为YAML格式,存在bruno.json则为Bru格式。
关于YAML格式的语法细节,请查看references/yaml-syntax.md。 关于Bru格式的语法细节,请查看references/bru-syntax.md

Workflow

工作流程

1. Create Collection Structure

1. 创建集合结构

Create the directory layout with the collection root file, environments, and organized request folders.
YAML format:
my-api-tests/
├── opencollection.yml          # REQUIRED: collection root
├── environments/
│   ├── Local.yml
│   ├── Staging.yml
│   └── Production.yml
├── Auth/
│   ├── folder.yml
│   └── Login.yml
└── Users/
    ├── folder.yml
    ├── Get Users.yml
    ├── Get User by ID.yml
    └── Create User.yml
Minimal
opencollection.yml
:
yaml
opencollection: 1.0.0

info:
  name: My API Tests
Bru format: Same structure but use
bruno.json
+
.bru
extensions. See references/bru-syntax.md.
创建包含集合根文件、环境配置和有序请求文件夹的目录布局。
YAML格式:
my-api-tests/
├── opencollection.yml          # 必填:集合根文件
├── environments/
│   ├── Local.yml
│   ├── Staging.yml
│   └── Production.yml
├── Auth/
│   ├── folder.yml
│   └── Login.yml
└── Users/
    ├── folder.yml
    ├── Get Users.yml
    ├── Get User by ID.yml
    └── Create User.yml
极简版opencollection.yml:
yaml
opencollection: 1.0.0

info:
  name: My API Tests
Bru格式: 结构相同,但使用bruno.json + .bru扩展名。请查看references/bru-syntax.md

2. Create Environment Files

2. 创建环境文件

YAML (
environments/Local.yml
):
yaml
variables:
  - name: baseUrl
    value: http://localhost:3000/api
  - name: apiKey
    value: ""
    secret: true
Bru (
environments/Local.bru
):
vars {
  baseUrl: http://localhost:3000/api
}

vars:secret [
  apiKey
]
YAML格式(environments/Local.yml):
yaml
variables:
  - name: baseUrl
    value: http://localhost:3000/api
  - name: apiKey
    value: ""
    secret: true
Bru格式(environments/Local.bru):
vars {
  baseUrl: http://localhost:3000/api
}

vars:secret [
  apiKey
]

3. Write Request Files with Tests

3. 编写带测试的请求文件

YAML format — a complete request with tests:
yaml
info:
  name: Get Users
  type: http
  seq: 1

http:
  method: GET
  url: "{{baseUrl}}/users"
  headers:
    - name: accept
      value: application/json
    - name: authorization
      value: "Bearer {{authToken}}"

runtime:
  assertions:
    - expression: res.status
      operator: eq
      value: "200"
    - expression: res.body
      operator: isArray
  scripts:
    - type: tests
      code: |-
        test("returns 200", function() {
          expect(res.status).to.equal(200);
        });

        test("returns array of users", function() {
          expect(res.body).to.be.an('array');
          expect(res.body).to.have.lengthOf.at.least(1);
        });

        test("each user has required fields", function() {
          res.body.forEach(user => {
            expect(user).to.have.property('id');
            expect(user).to.have.property('email');
          });
        });

settings:
  encodeUrl: true
Use assertions (declarative) for simple checks, tests scripts (Chai.js) for complex logic.
YAML格式——包含测试的完整请求:
yaml
info:
  name: Get Users
  type: http
  seq: 1

http:
  method: GET
  url: "{{baseUrl}}/users"
  headers:
    - name: accept
      value: application/json
    - name: authorization
      value: "Bearer {{authToken}}"

runtime:
  assertions:
    - expression: res.status
      operator: eq
      value: "200"
    - expression: res.body
      operator: isArray
  scripts:
    - type: tests
      code: |-
        test("returns 200", function() {
          expect(res.status).to.equal(200);
        });

        test("returns array of users", function() {
          expect(res.body).to.be.an('array');
          expect(res.body).to.have.lengthOf.at.least(1);
        });

        test("each user has required fields", function() {
          res.body.forEach(user => {
            expect(user).to.have.property('id');
            expect(user).to.have.property('email');
          });
        });

settings:
  encodeUrl: true
简单检查使用断言(声明式),复杂逻辑使用测试脚本(Chai.js)。

4. Chain Requests with Data Extraction

4. 数据提取实现请求链式调用

Extract data from one response and use it in subsequent requests:
YAML — Login request saving a token:
yaml
info:
  name: Login
  type: http
  seq: 1

http:
  method: POST
  url: "{{baseUrl}}/auth/login"
  body:
    type: json
    data: |-
      {
        "username": "{{username}}",
        "password": "{{password}}"
      }
  auth:
    type: none

runtime:
  scripts:
    - type: after-response
      code: |-
        bru.setEnvVar("authToken", res.body.access_token);
    - type: tests
      code: |-
        test("login successful", function() {
          expect(res.status).to.equal(200);
          expect(res.body).to.have.property('access_token');
        });
Then reference
{{authToken}}
in subsequent requests via
Bearer {{authToken}}
.
从一个响应中提取数据并在后续请求中使用:
YAML格式——保存令牌的登录请求:
yaml
info:
  name: Login
  type: http
  seq: 1

http:
  method: POST
  url: "{{baseUrl}}/auth/login"
  body:
    type: json
    data: |-
      {
        "username": "{{username}}",
        "password": "{{password}}"
      }
  auth:
    type: none

runtime:
  scripts:
    - type: after-response
      code: |-
        bru.setEnvVar("authToken", res.body.access_token);
    - type: tests
      code: |-
        test("login successful", function() {
          expect(res.status).to.equal(200);
          expect(res.body).to.have.property('access_token');
        });
随后在后续请求中通过
Bearer {{authToken}}
引用
{{authToken}}

5. Run Tests with bru CLI

5. 使用bru CLI运行测试

Install and run:
bash
npm install -g @usebruno/cli
安装并运行:
bash
npm install -g @usebruno/cli

Run entire collection

运行整个集合

cd my-api-tests && bru run --env Local
cd my-api-tests && bru run --env Local

Run specific folder

运行特定文件夹

bru run Auth --env Local
bru run Auth --env Local

Run with developer mode (for external packages, fs access)

开发者模式运行(支持外部包、文件系统访问)

bru run --env Local --sandbox=developer
bru run --env Local --sandbox=developer

Filter by tags

按标签筛选运行

bru run --tags=smoke --env Local
bru run --tags=smoke --env Local

Generate reports

生成报告

bru run --env Local
--reporter-html results.html
--reporter-junit results.xml
--reporter-json results.json
bru run --env Local
--reporter-html results.html
--reporter-junit results.xml
--reporter-json results.json

Pass secrets via CLI

通过CLI传递密钥

bru run --env Local --env-var API_KEY=secret123
bru run --env Local --env-var API_KEY=secret123

Parallel execution

并行执行

bru run --env Local --parallel
bru run --env Local --parallel

Data-driven testing

数据驱动测试

bru run --csv-file-path data.csv --env Local

**v3.0.0 breaking change**: Default is now Safe Mode. Use `--sandbox=developer` for developer mode features.
bru run --csv-file-path data.csv --env Local

**v3.0.0破坏性变更**:默认模式现在是安全模式。如需开发者模式功能,请使用`--sandbox=developer`。

6. Set Up CI/CD

6. 搭建CI/CD

See references/ci-cd.md for complete GitHub Actions workflows, matrix testing, and reporting patterns.
Minimal GitHub Actions workflow:
yaml
name: API Tests
on: [push, pull_request]

jobs:
  api-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "20"
      - run: npm install -g @usebruno/cli
      - name: Run API Tests
        working-directory: ./my-api-tests
        env:
          API_KEY: ${{ secrets.API_KEY }}
        run: bru run --env CI --reporter-html results.html --reporter-junit results.xml
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: test-results
          path: |
            ./my-api-tests/results.html
            ./my-api-tests/results.xml
Critical: Always set
working-directory
to the collection root in CI/CD.
完整的GitHub Actions工作流、矩阵测试和报告模式,请查看references/ci-cd.md
极简版GitHub Actions工作流:
yaml
name: API Tests
on: [push, pull_request]

jobs:
  api-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "20"
      - run: npm install -g @usebruno/cli
      - name: Run API Tests
        working-directory: ./my-api-tests
        env:
          API_KEY: ${{ secrets.API_KEY }}
        run: bru run --env CI --reporter-html results.html --reporter-junit results.xml
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: test-results
          path: |
            ./my-api-tests/results.html
            ./my-api-tests/results.xml
关键注意事项:在CI/CD中务必将
working-directory
设置为集合根目录。

Testing Patterns

测试模式

Assertions (Declarative) — Use for Simple Checks

断言(声明式)——用于简单检查

yaml
runtime:
  assertions:
    - expression: res.status
      operator: eq
      value: "200"
    - expression: res.body.success
      operator: eq
      value: "true"
    - expression: res.body.data
      operator: isJson
    - expression: res.headers.content-type
      operator: contains
      value: application/json
Operators vary slightly by Bruno version and editor surface. Check Bruno's current Assertions docs for the exact operator names supported by your version when writing declarative assertions.
yaml
runtime:
  assertions:
    - expression: res.status
      operator: eq
      value: "200"
    - expression: res.body.success
      operator: eq
      value: "true"
    - expression: res.body.data
      operator: isJson
    - expression: res.headers.content-type
      operator: contains
      value: application/json
操作符会因Bruno版本和编辑器界面略有不同。编写声明式断言时,请查看Bruno当前的断言文档,确认您使用的版本支持的准确操作符名称。

Tests (Chai.js) — Use for Complex Validation

测试(Chai.js)——用于复杂验证

yaml
runtime:
  scripts:
    - type: tests
      code: |-
        test("status and structure", function() {
          expect(res.status).to.equal(200);
          expect(res.body).to.be.an('object');
          expect(res.body).to.have.all.keys('id', 'name', 'email');
        });

        test("validates email format", function() {
          expect(res.body.email).to.match(/^[\w\-.]+@([\w-]+\.)+[\w-]{2,4}$/);
        });

        test("response time acceptable", function() {
          expect(res.responseTime).to.be.below(2000);
        });

        test("pagination works", function() {
          expect(res.body.data).to.be.an('array');
          expect(res.body.meta.total).to.be.a('number');
          expect(res.body.meta.page).to.equal(1);
        });
For the complete JavaScript API (
req
,
res
,
bru
objects), see references/javascript-api.md.
yaml
runtime:
  scripts:
    - type: tests
      code: |-
        test("status and structure", function() {
          expect(res.status).to.equal(200);
          expect(res.body).to.be.an('object');
          expect(res.body).to.have.all.keys('id', 'name', 'email');
        });

        test("validates email format", function() {
          expect(res.body.email).to.match(/^[\w\-.]+@([\w-]+\.)+[\w-]{2,4}$/);
        });

        test("response time acceptable", function() {
          expect(res.responseTime).to.be.below(2000);
        });

        test("pagination works", function() {
          expect(res.body.data).to.be.an('array');
          expect(res.body.meta.total).to.be.a('number');
          expect(res.body.meta.page).to.equal(1);
        });
完整的JavaScript API(
req
res
bru
对象)请查看references/javascript-api.md

Common Mistakes

常见错误

  1. Missing
    opencollection.yml
    (YAML) or
    bruno.json
    (Bru) at collection root
  2. Using
    meta:
    instead of
    info:
    in YAML request files
  3. Using script type
    test
    instead of
    tests
    (plural)
  4. Putting request-level fields (
    http:
    ,
    method:
    ) in
    opencollection.yml
  5. Forgetting
    working-directory
    in CI/CD steps
  6. Committing secrets — use
    secret: true
    in env files + CI/CD secrets
  7. Using
    |-
    for body data is required in YAML to preserve JSON formatting
  8. Missing
    seq
    number in
    info:
    — controls execution order
  9. Relying on
    folder.yml
    script/auth inheritance in CLI
    — Bruno's Sandwich execution order (Collection → Folder → Request) for
    before-request
    scripts may work in the Bruno GUI, but
    @usebruno/cli
    does NOT reliably inherit
    folder.yml
    scripts or auth settings to individual test files. Always add
    before-request
    scripts and
    auth
    blocks directly to each request file that needs them. The
    folder.yml
    is useful for documentation and GUI users, but CLI-driven tests must be self-contained.
  1. 集合根目录缺少opencollection.yml(YAML格式)或bruno.json(Bru格式)
  2. 在YAML请求文件中使用
    meta:
    而非
    info:
  3. 脚本类型使用
    test
    而非
    tests
    (复数形式)
  4. 将请求级字段(
    http:
    method:
    )放在opencollection.yml中
  5. CI/CD步骤中忘记设置
    working-directory
  6. 提交密钥——在环境文件中使用
    secret: true
    并结合CI/CD密钥管理
  7. YAML格式中需要使用
    |-
    来保留JSON格式的请求体数据
  8. info:
    中缺少
    seq
    编号——该编号控制执行顺序
  9. 依赖CLI中的folder.yml脚本/认证继承——Bruno的三明治执行顺序(集合→文件夹→请求)适用于
    before-request
    脚本在Bruno GUI中的运行,但
    @usebruno/cli
    并不可靠地将
    folder.yml
    脚本或认证设置继承到单个测试文件。务必将
    before-request
    脚本和
    auth
    块直接添加到每个需要它们的请求文件中。
    folder.yml
    对文档和GUI用户有用,但CLI驱动的测试必须是自包含的。

Script Execution Order

脚本执行顺序

Bruno supports two script flows:
  1. Sandwich (default): Collection
    before-request
    → Folder
    before-request
    → Request
    before-request
    Request is sent → Request
    after-response
    → Folder
    after-response
    → Collection
    after-response
  2. Sequential: Collection
    before-request
    → Folder
    before-request
    → Request
    before-request
    Request is sent → Collection
    after-response
    → Folder
    after-response
    → Request
    after-response
Request assertions and request
tests
run after the post-response scripts.
Bruno支持两种脚本流程:
  1. 三明治模式(默认):集合
    before-request
    → 文件夹
    before-request
    → 请求
    before-request
    发送请求 → 请求
    after-response
    → 文件夹
    after-response
    → 集合
    after-response
  2. 顺序模式:集合
    before-request
    → 文件夹
    before-request
    → 请求
    before-request
    发送请求 → 集合
    after-response
    → 文件夹
    after-response
    → 请求
    after-response
请求断言和请求
tests
在响应后脚本执行完毕后运行。

⚠️ CLI Inheritance Caveat

⚠️ CLI继承警告

The Sandwich/Sequential script execution order described above applies to Bruno GUI only. When running tests with
@usebruno/cli
(the CLI runner used in CI/CD), folder-level
before-request
scripts and
auth
settings are NOT reliably inherited by individual request files.
Impact: If a
folder.yml
contains a
before-request
script (e.g., to skip requests when an environment variable is
"false"
), request files in that folder will NOT inherit this logic when run via CLI.
Workaround: Duplicate the
before-request
logic into each individual request file that needs it:
yaml
undefined
上述三明治/顺序脚本执行顺序仅适用于Bruno GUI。 使用
@usebruno/cli
(CI/CD中使用的CLI运行器)运行测试时,文件夹级别的
before-request
脚本和
auth
设置无法可靠地继承到单个请求文件。
影响:如果
folder.yml
包含
before-request
脚本(例如,当环境变量为
"false"
时跳过请求),该文件夹中的请求文件在通过CLI运行时将不会继承此逻辑。
解决方法:将
before-request
逻辑复制到每个需要它的请求文件中:
yaml
undefined

In each request file that needs conditional skip:

在每个需要条件跳过的请求文件中:

runtime: scripts: - type: before-request code: |- const featureAvailable = bru.getEnvVar("featureAvailable"); if (featureAvailable === "false") { bru.runner.skipRequest(); } - type: tests code: |- test("returns 200", function() { expect(res.status).to.equal(200); });

**Same applies to auth**: Set `http.auth` in each request file, not just `folder.yml`.
runtime: scripts: - type: before-request code: |- const featureAvailable = bru.getEnvVar("featureAvailable"); if (featureAvailable === "false") { bru.runner.skipRequest(); } - type: tests code: |- test("returns 200", function() { expect(res.status).to.equal(200); });

**认证同理**:在每个请求文件中设置`http.auth`,而不仅仅是在`folder.yml`中。

Variable Precedence (Highest to Lowest)

变量优先级(从高到低)

  1. Runtime variables (
    bru.setVar()
    )
  2. Request variables
  3. Folder variables
  4. Collection variables
  5. Environment variables
Use
bru.getGlobalEnvVar()
for global environment values and
bru.getProcessEnv()
for OS process environment variables. They are not documented as part of the standard collection variable precedence chain.
  1. 运行时变量(
    bru.setVar()
  2. 请求变量
  3. 文件夹变量
  4. 集合变量
  5. 环境变量
使用
bru.getGlobalEnvVar()
获取全局环境值,使用
bru.getProcessEnv()
获取操作系统进程环境变量。它们不属于标准集合变量优先级链的文档范畴。

References

参考资料

  • YAML Syntax — Complete OpenCollection YAML format for requests, bodies, auth, headers, params, environments, folders, collections
  • Bru Syntax — Legacy
    .bru
    file format reference
  • JavaScript API — Full
    req
    ,
    res
    ,
    bru
    object API with runner control, cookies, utilities
  • CI/CD Integration — GitHub Actions workflows, report generation, matrix testing, environment secrets
  • YAML语法 —— 请求、请求体、认证、请求头、参数、环境、文件夹、集合的完整OpenCollection YAML格式说明
  • Bru语法 —— 旧版.bru文件格式参考
  • JavaScript API —— 完整的
    req
    res
    bru
    对象API,包含运行器控制、Cookie、工具函数
  • CI/CD集成 —— GitHub Actions工作流、报告生成、矩阵测试、环境密钥管理