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 與官方頁面為準,會隨時間變動。
| 項目 | Bruno | Hoppscotch | Insomnia | Postman |
|---|---|---|---|---|
| GitHub 星數 | 48,025 | 80,731 | 40,110 | 閉源 |
| 授權 | MIT | MIT | Apache-2.0 | 專有商業 |
| 建立年份 | 2022 | 2019 | 2016 | 2012(Chrome 擴充起家) |
| 主要形態 | 桌面 App + CLI | 網頁 + 桌面 + CLI | 桌面 App | 桌面 App + 雲端 |
| collection 存哪 | 本機資料夾(Git 友善) | 本機/雲端可選 | 本機/雲端/Git 儲存 | 雲端為主 |
| CLI | bru run(npm) | hopp CLI | inso CLI | newman |
| 適合誰 | 想把 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「假綠」的坑
--tags沒命中任何 request 時,結果是 PASS。實測bru run --env local --tags smoke(這份 collection 沒有smoke標籤)回傳Requests: 0、Status: ✓ PASS、exit code 0。如果你用 tag 分艙跑,請在 CI 額外檢查「Requests > 0」,不然整批測試被靜默跳過你也不會知道。- 別把
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 匯入器,但變數、測試腳本的轉換還是要人眼檢查。
站內延伸閱讀
- Postman 桌面版自己把外掛裝進 Codex 與 Claude Code——這次事件的完整證據鏈,也是「為什麼純文字、本機優先」值得多付一點學習成本的理由。
- FastAPI 完整教學 2026:路由、Pydantic v2 到部署——寫好 API 之後,下一步就是用 Bruno 這種工具把它串成可重複的測試。
- Docker Compose 完整教學 2026:compose.yaml 到 AI models——把被測服務與 collection 一起用 compose 起起來,本機 CI 就完成一半。
利益揭露:本文無聯盟連結,Bruno 為 MIT 開源專案,免費使用。
