Bruno 完整教學 2026:Git 友善的開源 API 客戶端

Bruno 是 48,000 星的 MIT 開源 API 客戶端,把 request 存成純文字 YAML,collection 直接進 Git 跑 CI。本文用官方 starter collection 實測 bru CLI 4.3.0,整理安裝指令、目錄結構、環境與 secret 變數、斷言寫法與 GitHub Actions 範例,並附上兩個會讓 CI 假綠的坑。

  • Dennis
  • 7 分鐘閱讀
Bruno 完整教學 2026:Git 友善的開源 API 客戶端

Bruno 是一套 MIT 授權的開源 API 客戶端,把每個 request 存成純文字檔(YAML 或 .bru),collection 就是一個資料夾,可以直接進 Git、走 code review、在 CI 用 bru run 跑測試。本次實測版本為 CLI 4.3.0,官方 starter collection 五個 request 全數通過、耗時 1,070 ms。

為什麼要換:三個痛點

痛點雲端優先的 API 客戶端Bruno 的做法
誰能改你的 collection存在廠商雲端,改了什麼只能看活動紀錄存在你的磁碟,改了就是一個 git diff
能不能 code review沒有 diff,只能口頭同步每個 request 一個檔案,PR 裡逐行看
能不能在 CI 跑要連雲端、要帳號、要額度bru run 純本機,Node.js 18+ 就能跑
副檔名綁定匯出成一包大 JSON,衝突解不開一個 request 一個檔案,衝突範圍最小
工具本身的權限桌面版可能主動改你其他工具的設定檔案在你手上,要看得到才相信

最後一列不是空話。2026 年 10 月底就發生過一次:Postman 桌面版自己把外掛裝進 Codex 與 Claude Code,還代為核准啟動 hook。這不代表所有商業工具都會這樣做,但它說明了一件事:檔案放在自己硬碟、格式是純文字,本身就是一種風險控制。

一表看懂:四個常見選擇

以下數字以 2026-11-03 查詢 GitHub API 與官方頁面為準,會隨時間變動。

項目BrunoHoppscotchInsomniaPostman
GitHub 星數48,02580,73140,110閉源
授權MITMITApache-2.0專有商業
建立年份2022201920162012(Chrome 擴充起家)
主要形態桌面 App + CLI網頁 + 桌面 + CLI桌面 App桌面 App + 雲端
collection 存哪本機資料夾(Git 友善)本機/雲端可選本機/雲端/Git 儲存雲端為主
CLIbru run(npm)hopp CLIinso CLInewman
適合誰想把 API 測試當程式碼管的人想直接在瀏覽器試 API 的人需要多協定+企業功能的人團隊已在用、需要完整平台的人

Bruno 的星數不是最高,但它對「collection 就是程式碼」這件事最徹底——連 collection 的根目錄都只是一份 opencollection.yml。

安裝

桌面 App 走各平台原生套件,指令都來自官方文件:

# macOS(Homebrew)
brew install bruno

# Windows(winget / Chocolatey / Scoop 三者擇一)
winget install Bruno.Bruno
choco install bruno
scoop install bruno

Linux 有 AppImage、.deb、.rpm,也有 APT、Flatpak、Snap 的來源設定;v4.0.0 起 macOS 另外提供 .pkg 安裝包,方便 MDM 大量部署(sudo installer -pkg bruno_<version>_<arch>_mac.pkg -target /)。

CLI 另外裝,需要 Node.js 18 以上:

npm install -g @usebruno/cli
bru --version

Collection 長什麼樣

建立 collection 時可以選 YAML 或 BRU 兩種格式,官方從 v3 起就建議新專案用 YAML——它遵循 Bruno 自己提出的 OpenCollection 規格,好處是各種工具都能讀(yamllint、prettier、任何 YAML 函式庫)。

實際 clone 官方 starter collection 後,目錄結構是這樣:

bruno-starter-guide/
├── opencollection.yml          # collection 根目錄,包含名稱與 ignore 規則
├── environments/
│   └── local.yml               # 環境變數(baseURL 等)
├── 01-github-api.yml
├── 02-echo-bru.yml
├── 03-script-request.yml
├── 04-auth-request.yml
└── 05-visualizer.yml

opencollection.yml 本身只有五行:

opencollection: 1.0.0

info:
  name: Bruno Starter guide
bundled: false
extensions:
  bruno:
    ignore:
      - node_modules
      - .git

單一 request 就是一份完整、可讀的 YAML。這是 starter collection 裡「打 GitHub API」那一個:

info:
  name: 01-github-api
  type: http
  seq: 1

http:
  method: GET
  url: https://api.github.com/users/usebruno
  auth: inherit

settings:
  encodeUrl: true
  timeout: 0
  followRedirects: true
  maxRedirects: 5

每個 request 一個檔案是它跟傳統匯出格式最大的差別:五個人同時改五個 request,Git 不會撞在一起。

環境變數與 secret

environments/local.yml 定義環境,request 裡用 {{baseURL}} 取用:

name: local
variables:
  - name: baseURL
    value: https://echo.usebruno.com

要新增環境就多一個檔案(stg.yml、prd.yml),bru run --env prd 切換。4.2.0 之後環境還支援 extends 繼承——prd.yml 只寫跟 parent 不同的值,其餘沿用;但要注意這個 key 只有 4.2.0 以上認得,用舊版打開會把 extends 丟掉。

密鑰的部分,Bruno 讓你在環境變數上勾 secret,值不會寫進環境檔,改存在本機並用 OS 層級加密(沒有就用 AES-256)。環境檔裡只會留下這個標記:

vars:secret [
  baseUrl
]

這也是為什麼 collection 可以安心 push:檔案裡根本沒有金鑰。v4.0.0 之後 secret 變數另外開一個頁籤,避免跟一般變數搞混;匯出 collection 時也不會帶出去。

測試與斷言:兩種寫法

第一種是宣告式斷言(Assert 頁籤,或 YAML 的 runtime.assertions),適合單純的狀態碼/欄位檢查:

runtime:
  assertions:
    - expression: res.status
      operator: eq
      value: "200"
    - expression: res.body.name
      operator: isString

第二種是寫程式,type: tests 區塊用 Chai 的 expect:

runtime:
  scripts:
    - type: tests
      code: |-
        test("should receive the token", function() {
          expect(res.status).to.equal(200);
          expect(res.body.token).to.be.a('string');
        });

還有 type: after-response(把 token 存進變數給下一個 request 用,bru.setVar("token", res.body.token))與 type: before-request(動態改 header、body)。request chaining 就是靠這兩段串起來的:登入拿 token → 存變數 → 下一個 request 用 {{token}}。

CLI 實跑:真實輸出

本次在一台 Linux 機器上裝 @usebruno/cli 4.3.0,直接 clone 官方 starter collection 跑:

git clone https://github.com/bruno-collections/bruno-starter-guide.git
cd bruno-starter-guide
bru run --env local

實際輸出(逐字):

01-github-api (200 OK) - 188 ms
02-echo-bru (200 OK) - 105 ms
Tests
   ✓ body has title
   ✓ 200 status code
Assertions
   ✓ res.status: eq 200
   ✓ res.body.title: eq Bruno
...
📊 Execution Summary
│ Requests      │ 5 (5 Passed) │
│ Tests         │     2/2      │
│ Assertions    │     2/2      │
│ Duration (ms) │     1070     │

幾個實測得到的細節:

  • 單檔執行:bru run 01-github-api.yml --env local 只跑那一個 request,適合除錯。
  • 覆寫變數:--env-var baseURL=https://httpbin.org/anything 可以臨時換 host,不用改檔案。實測把它指到 httpbin 後,原本的測試立刻變成 1/2,✕ expected undefined to equal 'Bruno'——這也順便驗證了斷言真的有在跑。
  • 失敗會回非零:上面那個失敗案例,bru run 的 exit code 是 1,CI 可以直接靠它卡關。
  • 報告三種格式:--reporter-json、--reporter-junit、--reporter-html,實測產出 14 KB / 1.3 KB / 44 KB 三個檔,可以給 Jenkins、GitLab、GitHub Actions 各自吃。

放進 CI:GitHub Actions 範例

name: api-tests

on:
  pull_request:
    paths:
      - 'api-collection/**'

jobs:
  bruno:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm install -g @usebruno/cli
      - name: Run API collection
        working-directory: api-collection
        run: bru run --env ci --reporter-junit results.xml
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: bruno-results
          path: api-collection/results.xml

兩個會讓 CI「假綠」的坑

  1. --tags 沒命中任何 request 時,結果是 PASS。實測 bru run --env local --tags smoke(這份 collection 沒有 smoke 標籤)回傳 Requests: 0、Status: ✓ PASS、exit code 0。如果你用 tag 分艙跑,請在 CI 額外檢查「Requests > 0」,不然整批測試被靜默跳過你也不會知道。
  2. 別把 bru run 的輸出接給 tail 再取 $?。bru run ... | tail -20; echo $? 拿到的是 tail 的結束碼,永遠 0。要嘛直接讓它跑(輸出會被 CI log 完整保留),要嘛 bru run ... > run.log 2>&1; echo EXIT=$?。

已知限制/沒做的事

把界線講清楚,比多寫三個優點有用:

  • GUI 沒有全程實際操作:本文的檔案結構、環境、secret 說明取自官方文件與官方 starter collection 的實際內容,但「用滑鼠建立 collection、勾 secret 勾選框」這些步驟沒有在桌面版逐步截圖驗證。
  • 沒有測 secret manager 整合:Vault、AWS Secrets Manager、Azure Key Vault 這條路(--secrets-env-file)未實測,只用過檔案內的 secret 變數。
  • 沒有測多協定:GraphQL、gRPC、WebSocket、MQTT、Socket.IO 都只有看文件,未實際發過請求。
  • 沒有在真的 GitHub Actions 上跑過:上面的 workflow 是照 CLI 行為寫的,bru run 與報告產生在本機驗證過,但整段 workflow 沒有 push 到 repo 觸發過。
  • 資料驅動(CSV/JSON iteration)沒跑:--csv-file-path、--json-file-path、--iteration-count 只確認文件存在,未實測。
  • 星數會變:表中數字是 2026-11-03 的查詢結果,Bruno 近期活躍(同日仍有 commit),過幾週請以官網為準。

另外兩個容易被忽略的設計取捨:一是 Bruno 的 collection 需要你自己管資料夾位置(預設在 ~/Documents/bruno),不像雲端工具幫你同步——這是優點也是負擔;二是 .bru 與 YAML 兩套格式並存,團隊若混用會很痛苦,建議一開始就統一。

誰適合、誰不需要

適合:把 API 測試當程式碼管、想在 PR 裡看 collection diff、已經有 CI 想加一層 API 冒煙測試、公司政策不允許把 request 內容放上第三方雲端的團隊。

不需要:只需要偶爾點幾下看回應、需要完整 API 平台(mock + monitor + 文件 + 團隊權限一次到位)、或已經有成熟 Postman workspace 且不打算搬的人——遷移成本比想像高,Bruno 有 Postman/Insomnia 匯入器,但變數、測試腳本的轉換還是要人眼檢查。

站內延伸閱讀

利益揭露:本文無聯盟連結,Bruno 為 MIT 開源專案,免費使用。

📬 訂閱 most.tw 電子報

每週精選 AI 工具教學與技術乾貨,直接送到你的信箱。免費、隨時可退訂。

訂閱即表示同意收到 most.tw 電子報,隨時可一鍵退訂。

💬 有問題想討論?加 LINE 聯絡我

歡迎透過 LINE 官方帳號直接留言,我會盡快回覆你的問題。

加入 LINE 好友