
天天调试接口的人估计都有过这种瞬间需求方催得急你打开 Postman 想发个请求确认结果结果先弹更新提示再要求登录账号好不容易进主界面又开始转圈加载工作区。明明只是想发一个 POST 请求啊硬生生被流程拖了半分钟。我最近换掉 Postman 之后才意识到一个道理很多时候我们不是需要更强大而是需要更轻快。这篇博文要聊的就是这样一款替代品——安装包 10MB 级别冷启动接近 1 秒内集合数据全部以普通文件存放在本地它就是开源 API 客户端 Bruno。如果你也在受 Postman 的体积膨胀、登录墙和云同步干扰这篇文章应该能帮你省下不少折腾时间。1. 为什么我一个天天用 Postman 的人最后换成了它先声明一件事我不是来否定 Postman 的。论功能丰富度和生态完整度Postman 到今天依然是标杆很多人从入行就用它脚本、环境、团队工作区、云端监控全都跑在上面。但问题恰恰出在丰富上面——当一个工具从轻量客户端慢慢长成重平台日常开发里那些高频操作就被层层包裹住了。1.1 Postman 的三个变化让我萌生退意第一个变化是强制登录。2023 年之后Postman 几乎已经把必须先登录才能打开主界面写进了产品逻辑。你装好客户端第一步不是建请求而是注册账号、验证邮箱、登录同步。对个人开发者和内部测试来说这个账号体系带来的价值很小门槛感却很强。第二个变化是体积和资源占用。现在的 Postman 安装包动辄几百 MB安装完部署文件更多运行起来还要同步各种工作区数据、检查更新、加载插件渲染。我亲测的情况是内存占用轻松过 500MB笔记本风扇也跟着起哄。就为了测一个接口这代价属实有点高。第三个变化是中文网络环境下的下载与安装源混乱。搜postman 下载能看到一堆第三方站点点进去可能是带捆绑的安装包搜postman 免登录版本、postman 汉化出来的内容更让人不敢下手。这里我多说一句非官方源的 Postman 包我是强烈不建议用的因为这类工具会直接接触你的请求数据和 Host 信息一旦被别人动过手脚后果比工具卡顿严重得多。日常 90% 的接口调试说到底就是填 URL、加 Header、写 Body、看响应、偶尔做点断言和提取变量。这套流程完全不需要一个重度平台来承载所以我把目光投向了轻量替代方案。1.2 Bruno 是不是你想要的替代品Bruno 是一个开源 API 客户端源码在 GitHub 的 usebruno/bruno 仓库MIT 许可证个人用和商用都不受限制。它最吸引我的是两个数字安装包 10MB 级别不同平台略有差异但和几百 MB 的 Postman 完全不在一个量级冷启动在 SSD 上基本 1 秒内几乎是点击图标就看到主界面更重要的是Bruno 不需要登录没有任何云端账号体系。你下载、打开、开始建请求整个过程没有任何账号注册、激活码、同步等待。这个打开就能用的体验用过就回不去了。它不是想全面取代 Postman而是在个人开发 / 小团队 / 自动化测试这个最常用的场景里提供了一个轻得多、快得多、数据完全可控的选项。2. 集合就是文件夹Bruno 的数据模型有多舒服你可能觉得启动快、体积小只是体感层面的优化但 Bruno 和 Postman 之间真正深层的差异是数据模型。这个差异决定了你在日常维护接口集合、做自动化测试时的整个工作流。2.1 一个请求就是一个 .bru 文件Postman 里的集合是一坨经过封装的 JSON 数据靠客户端数据库管理。Bruno 完全反过来一个请求就是一个以.bru结尾的纯文本文件字段结构明明白白。我随便打开一个文件给你看meta { name: Get User type: http seq: 1 } get { url: https://jsonplaceholder.typicode.com/users/1 body: none auth: none }你想改请求方法、URL、Header直接在文件里改都行。一个集合在磁盘上就是一个目录my-api-tests/ ├── collections/ │ ├── users/ │ │ ├── create-user.bru │ │ ├── delete-user.bru │ │ └── get-user.bru └── environments/ ├── dev.bru └── prod.bru这意味着什么意味着你的所有接口定义、环境变量、断言脚本都可以直接放进 Git 仓库享受git diff、git log、git merge的完整能力。团队里任何一个成员改了接口请求你不需要等他把文件导出发过来直接看 PR 里的 diff 就行。这在 Postman 的工作区里是做不到这么丝滑的至少你得经过同步 / 导出 / 导入这几道工序。2.2 环境变量和脚本都以文本文件保存Bruno 的环境变量同样存成文件。一个典型的环境文件长这样vars { baseUrl: https://jsonplaceholder.typicode.com apiKey: xxxxxx }脚本呢Bruno 把脚本分成两块请求发送前执行的 Request Script和响应返回后执行的 Test Script。写法上它就是普通 JavaScript只不过 API 名称从 Postman 的pm.*变成了bru.*。因为这个设计所有脚本逻辑都能被 Git 跟踪到谁在哪个请求里加了断言、提取了哪个字段在提交记录里看得一清二楚。2.3 为什么本地文件模型能带来秒开很多人以为 Bruno 启动快是因为没用 Electron其实它底层也走 Electron真正让它快起来的不是框架而是产品设计。Postman 客户端启动时要做什么连接账号体系、初始化本地数据库索引、检查云端工作区变更、拉取可能的同步任务、加载各类服务模块。这一套重流程走完快也得转几秒。Bruno 启动时只做一件事扫描你指定的本地目录把.bru文件读出来形成集合树。没有后台服务没有数据库迁移没有常驻进程没有账户体系。打开就是打开数据全在本地文本里。轻不只是安装包重量而是整个运行时都没有背着多余包袱。3. 实操从下载安装到跑通第一个带断言的请求这一节我把完整上手流程拆出来照着做基本十几分钟就能跑通。涉及的命令、脚本都是我用过的不同版本可能有细微差异但大方向不变。3.1 安装Windows / macOS / Linux 三种方式最推荐的方式是去 GitHub Releases 页下载对应平台安装包也可以走官网下载。Windows下载 Setup 安装包运行。安装完毕打开无管理员限制也没有后台服务常驻不想要了直接卸载干净。10MB 出头的安装包下载体验和 300MB 安装包完全两回事。macOS下载 dmg 后把应用拖入 Applications。因为不是 App Store 渠道首次打开如果提示来源问题右键图标选择打开即可。LinuxUbuntu下载.deb包后用一条命令装sudo apt install ./bruno_*.deb也可以用 AppImagechmod x后直接运行连安装都省了。装上以后打开主界面左侧是集合树中间是请求编辑器右侧是响应区底部可以有脚本和响应详情 Tab整体走极简风格即使界面是英文也没什么障碍一眼就能看懂无需汉化。3.2 第一个 GET 请求公开 API 练手我习惯用 JSONPlaceholder 这种公开 API 做演示它稳定、免费、不要求认证很适合第一次测试。流程如下点左上角加号创建新集合命名为demo。在集合下新建请求填名称Get Post。请求 URL 填https://jsonplaceholder.typicode.com/posts/1点击 Send右侧立刻出现响应 JSON。这时候你点响应区旁边的 Headers、Cookies能直接看到返回头信息。对日常联调来说这个发请求 - 看响应的核心链路非常干净。3.3 环境变量不再把 URL 硬编码进每个请求项目接口通常会区分 dev、test、prod 环境URL 前缀各不相同。Bruno 里处理方式和 Postman 一样用双花括号包裹变量名。你可以在环境 Tab 里新建一个环境文件vars { baseUrl: https://jsonplaceholder.typicode.com }然后在请求里填{{baseUrl}}/posts/1切换环境的入口就在右上角环境下拉框选哪个环境请求里对应的变量就取哪个值。这里有个很实用的细节环境文件本身也是普通文本不同环境变量之间的差异可以直接git diff对比比如看看 dev 和 prod 的域名是不是搞反了一目了然。3.4 POST 请求、断言、提取返回值一气呵成我们进一步做个 POST 请求并把断言和返回值提取一起演示。请求配置URL{{baseUrl}}/postsMethodPOSTBody 选择 JSON填写{ title: Bruno Test, body: hello from bruno, userId: 1 }发送后应该返回 201 和一条带 id 的创建记录。接下来在 Test Script 标签里写断言bru.test(status code is 201, () { bru.expect(res.getStatus()).to.equal(201); }); const body res.getBody(); bru.test(response has id field, () { bru.expect(body).to.have.property(id); }); bru.setEnvVar(createdPostId, body.id);前两个用例分别校验 HTTP 状态码和响应体字段第三个bru.setEnvVar会把返回 JSON 中的id提取出来存成环境变量。之后你在这个集合里新增一个根据 ID 查详情的请求URL 直接写{{baseUrl}}/posts/{{createdPostId}}这里恰好对应很多人搜过的postman 提取返回值和postman 断言获取 body 内容——在 Bruno 里这两个需求用上面的脚本就能实现原理和 Postman 基本一致只是 API 前缀从pm换成了bru。3.5 导出 cURL 和导入 Postman 集合日常贴请求到对话里经常需要把请求转成 cURL 命令。Bruno 里右键请求文件或点击请求菜单选择导出 cURL即可复制出完整的 curl 命令。这个功能直接对标postman 怎么导出 curl的诉求。反过来老项目想从 Postman 迁过来Bruno 支持导入 Postman Collection v2.1 JSON在 File Import 里选择 Postman Collection 文件Bruno 会自动转换成本地.bru文件。注意一点导入的是请求基本信息Postman 里的脚本和断言不会被自动翻译成bru.*语法这块后面手动改。4. 用 Bruno CLI 做自动化测试和持续集成单个请求手点只是基本功。接口测试的价值在于回归在于每次代码变更后自动跑一遍。Bruno 自带官方 CLI可以脱离图形界面执行集合这正好踩中postman 做自动化接口测试和postman 持续集成这两个高频需求点而且流程比 Newman 简单。4.1 安装 CLI 并跑通第一个集合前提是机器上已经有 Node.js。然后用 npm 全局安装npm install -g usebruno/cli安装后在存放集合目录的根目录执行bru run .CLI 会扫描该目录下的所有.bru文件并按顺序执行。也可以指定集合目录、指定环境bru run ./collections/users --env dev执行结果会在终端里输出每个请求的通过/失败情况。最核心的一点只要有一个断言不通过CLI 就以非 0 退出码结束。非 0 退出码对 CI 系统来说就是任务失败所以它天生适合接入流水线。4.2 在 GitHub Actions 里跑接口测试接入 GitHub Actions 只需要一个简单的 workflow 文件我把最小可用版本贴出来name: api-regression on: push: branches: [main] jobs: run-api-tests: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - uses: actions/setup-nodev4 with: node-version: 20 - run: npm install -g usebruno/cli - run: bru run ./collections --env prod --output report.json逻辑很清楚每次 push 到 main 分支就签出代码、装一下 CLI、直接跑集合目录。--output report.json可以生成结果文件方便后续把报告作为 Artifact 上传或供其他脚本解析。这套流程比 Newman 少了一个大步骤不用手动导出一个 collection JSON 和一个 environment JSON也不用担心导出的文件和仓库里的最新代码不同步。集合本身就在仓库里PR 变了接口文件CI 跑的自然就是最新的接口定义。4.3 给集合注入环境变量与密钥环境文件里写敏感信息再提交到 Git 库里这是大忌。Bruno CLI 支持通过命令行覆盖变量bru run ./collections --env prod --env-var apiKey$API_KEY其中$API_KEY可以从 CI 平台的 Secrets 读取。比如在 GitHub Actions 里- run: bru run ./collections --env prod --env-var apiKey${{ secrets.API_KEY }}这样环境文件里可以只保留不敏感的基础配置真正的密钥交给 CI Secrets 注入既安全又灵活。4.4 和 Newman 的真实差异在哪Postman 的 Newman 功能也不弱但使用链路是这样的你需要在 Postman 工作区维护 collection修改接口后手动导出 JSON再单独维护 environment JSON最后在仓库里放这两个文件。一旦有人忘记同步CI 跑的接口定义就不是最新的。Bruno 直接把 collection 变成仓库的一部分接口变更是普通的代码变更走正常的 Code Review 流程。不需要额外的同步步骤这是它做自动化测试时最舒服的一点。5. 切换期避坑哪些坑我先替你踩过了用 Bruno 一段时间后有优点也有边界。这里把踩过的坑和权衡实话实说免得你切换后措手不及。5.1 从 Postman 迁移脚本不是复制粘贴Postman 里最常见的断言长这样pm.test(Status code is 200, function () { pm.response.to.have.status(200); }); const jsonData pm.response.json(); pm.variables.set(id, jsonData.id);Bruno 里对应写法是bru.test(Status code is 200, () { bru.expect(res.getStatus()).to.equal(200); }); const body res.getBody(); bru.setEnvVar(id, body.id);差异点主要集中在三处pm.test换成bru.testpm.response.status/pm.response.json()换成res.getStatus()/res.getBody()pm.variables.set换成bru.setEnvVar如果你有大量历史脚本建议不要手动逐个改。更高效的做法是先用脚本做一次正则替换把pm.test批量替换成bru.testpm.response.to.have.status这类语义再手工调整。因为断言库不完全等价别指望 100% 自动迁移。5.2 断言库和变量作用域的差异Bruno 的断言是类 Chai 的链式风格能力覆盖日常接口校验足够但 Postman 里有些顺手用的断言片段在 Bruno 里需要换种写法。比如 Postman 的pm.response.to.have.jsonBody()在 Bruno 里改成用bru.expect(...).to...的表达式。变量作用域也要注意。Bruno 的环境变量按环境划分请求脚本中bru.setEnvVar写入的变量在当前会话里会覆盖对应环境的同名变量。这个行为和 Postman 的pm.variables.set体验类似但如果你同时开了多个环境写变量时要看清当前选中的是哪个环境否则很容易出现明明写了变量另一个请求却读取不到的情况。5.3 不适合切换到 Bruno 的几种情况先说结论不是一个什么都要换的工具迁移指南它有自己的舒适区也有明显不擅长的边界。Postman 的团队工作区、云端共享、在线 Mock Server、Cloud Monitor 定时监控、API 网络社区在线接口库这些能力 Bruno 目前都没有对应的云端方案。如果你的团队重度依赖这些功能并且已经有成体系的 Postman 团队流程强切 Bruno 会很难受。另外如果你需要调试 WebSocket、GraphQL Subscription、Socket.IO 这类相对特殊的协议Bruno 的 GUI 支持力度也不如 Postman 丰富建议先在官方文档确认你要用的协议是否有对应请求类型。5.4 我的安全与团队协作建议结合我在团队里实际用下来的经验给你几条实在的建议不要把敏感环境变量提交进 Git 仓库。环境文件里的密钥用占位符录入真正的值由本地环境文件或 CI Secrets 注入。在项目根目录建立 api-tests 目录并纳入 PR 流程。把 Bruno 集合目录放在业务代码仓库里后端改接口、前端改请求都能在同一个 PR 里看到变更比单独的测试仓库更顺手。给惯用的集合建立命名规范。建议按模块分目录一个请求一个文件文件内部加上meta.docs字段写清楚业务含义和数据说明别让.bru文件变成第二个不可读的接口文档。最后分享一个实用小技巧我自己折腾下来最喜欢的一个用法是把 Bruno 的集合目录直接软链到一个业务项目里。比如前端项目根目录下建一个api-tests做完接口调试顺手把集合文件维护好后端联调结束、接口回归测试也顺便留在了仓库里。以后每次迭代跑一遍bru run .就能快速确认历史接口有没有被改坏。Bruno 也许不是最全能的 API 客户端但如果你像我一样只是想要一个打开就能用、不绑架你账号和内存的轻量工具它绝对值得你装来试一个晚上。