はじめに
こんにちは!ソーイ株式会社の工藤です。
日常業務でAPIを実装し、Postmanで動作確認、手動のAPI単体テストを行っています。テスト量が増え、工数が膨大になっていることに課題を感じ、自動化したいと思うようになりました。
本記事では、Postmanと比較しながらBrunoの基本機能を整理し、CursorなどAIエージェントを搭載したエディタのターミナルからテストを自動実行するところまでをまとめます。実行環境としてCursorを前提にしている点にご留意ください。
結論
Brunoを使うと、Postmanの Collection・Environment・Tests に相当する機能を、すべてプレーンテキストの.bruファイルで管理できます。コレクションはGitでそのままバージョン管理でき、CLI(bru)が標準で使えるため、GUIで作ったテストをそのままCursorのターミナルやCI/CDパイプラインで自動実行できます。「動作確認のたびに手動でPostmanを開いてSendを押す」作業から離れ、テスト実行を自動化して工数を削減できるのが最大のメリットです。
想定読者
- Postmanでのリクエスト送信・Collection管理には慣れているが、Brunoは触ったことがないエンジニア
- API単体テストを自動化して、手動確認にかかる工数を減らしたい人
- Cursorなど、AIエージェントを搭載したエディタを普段使っている人
この記事でわかること
- Brunoの概要
- PostmanとBrunoの違い
- Brunoの始め方
- Brunoで実務的によく使う基本機能5つ
- Cursorのターミナルからテストを自動実行する方法
Brunoとは
Bruno(usebruno)は、GitフレンドリーかつオフラインファーストなオープンソースのAPIクライアントです。PostmanやInsomniaのようなGUIアプリケーションでAPIリクエストを組み立てられる一方で、コレクションの実体はクラウド上のデータではなく、プロジェクトのファイルシステムに保存される.bruというプレーンテキストファイルです。この「テキストファイルとして手元に残る」という設計が、Gitでのバージョン管理やCLIでの自動実行のしやすさにつながっています。
PostmanとBrunoの違い
PostmanもBrunoも無料で使えるツールですが、無料の範囲内でも運用のしやすさに違いがあります。
- コレクションの保存場所:Postmanはログインを前提としたクラウド同期が中心で、オフラインやローカル完結での運用がしづらい設計です。Brunoはコレクションをプロジェクトのファイルシステムにそのまま保存する、ローカルファーストの設計になっています。
-
Gitでの差分管理:Postmanのコレクションをエクスポートした
.jsonは差分が読みにくく、PRでのレビューに向きません。Brunoの.bruファイルはプレーンテキストなので、そのままコミットしてdiffレビューができます。 -
CLIでの自動実行:Postmanは別ツールのNewmanやCollection Runnerを使う必要がありますが、Brunoは
bruという公式CLIを標準搭載しており、GUIで作ったテストをそのままターミナルから実行できます。
無料範囲での機能比較表
| 項目 | Postman(無料プラン) | Bruno(無料・OSS) |
|---|---|---|
| コレクションの保存形式 | 独自形式(基本はクラウド同期、.jsonエクスポートも可) |
.bru(プレーンテキスト) |
| Gitでの差分管理 | エクスポートした.jsonは差分が読みにくい |
.bruファイルをそのままコミットでき、diffがそのままレビュー可能 |
| オフライン・ローカル完結 | クラウド同期が前提の設計 | ローカルファーストで、クラウド同期なしでも完結 |
| CLIによる自動実行 | Newman(別ツール)またはCollection Runner |
bru(公式CLI)を標準搭載 |
| テストスクリプトの言語 | JavaScript(Chaiライクなpm.test) |
JavaScript(Chaiライクなtest/expect) |
| ライセンス | プロプライエタリ(SaaS、無料プランあり) | オープンソース(MIT) |
各ツールの無料プランの範囲・仕様は変更される可能性があるため、最新情報は公式サイトでご確認ください。
Brunoの始め方
Brunoは公式サイトからインストーラーをダウンロードすれば、すぐに使い始められます。Postmanと違ってアカウント登録は不要です。
- Bruno公式サイトにアクセスし、OSに応じたインストーラーをダウンロードする。
- インストール後にアプリを起動し、「Create Collection」から新しいコレクションを作成する。
- 保存先にプロジェクトのリポジトリ配下を指定する(これだけでGit管理下に置かれます)
図: サイドバーの「+」からコレクション作成メニューを開いたところ
ここまでできれば、次の章で紹介する基本機能をすぐに試せます。本記事のサンプルには、誰でも自由に使える無料のテスト用API「JSONPlaceholder」を使用しています。
Brunoでできることの基本
Brunoには多くの機能がありますが、Postmanから移行する上でまず押さえておきたい5つに絞って紹介します。
1. リクエストの作成・送信
Brunoの基本動作です。GUI上でMethod・URL・Headers・Bodyを設定して「Send」を押すとリクエストを送信できます。コレクションを右クリックすると、新規リクエストの作成メニューが表示されます。
裏側では、この内容が以下のような.bruファイルとしてそのまま保存されます。
meta {
name: Create User
type: http
seq: 1
}
post {
url: https://jsonplaceholder.typicode.com/users
body: json
}
headers {
Content-Type: application/json
}
body:json {
{
"name": "Taro Yamada",
"email": "taro@example.com"
}
}
Sendを押すと、ステータスコード201 Createdとともに、IDが付与されたデータがレスポンスとして返ってきます。PostmanのようにGUIで組み立てるだけでリクエストが作れる点は同じですが、Brunoでは組み立てた内容がそのままファイルとしてGit管理できる点が異なります。
図: 左がリクエストBody、右がレスポンス。IDが自動採番されて返ってきている
2. Authorization(認証)の設定
動作確認の際にトークン認証が必要になるケースは少なくありません。BrunoにもPostmanと同様、専用の「Auth」タブが用意されています。
- リクエストの「Auth」タブを開く
- Typeで「Bearer Token」を選択
- Tokenの欄に取得したアクセストークンを貼り付ける
これだけで、リクエスト送信時に以下のヘッダーが自動的に付与されます。
Authorization: Bearer eyJh...
毎回トークンを手入力するのは手間なので、後述のEnvironmentと組み合わせて{{access_token}}のように変数化しておくのがおすすめです。
3. Collectionでリクエストをまとめて管理
作成したリクエストは、Postmanと同じく「Collection」というフォルダ単位で整理できます。「ユーザー管理API」というコレクションの中に「ユーザー作成」「ユーザー一覧取得」「ユーザー削除」といったリクエストをまとめておけば、必要なときにすぐ呼び出せます。
Brunoの場合、コレクションの実体はディレクトリそのものなので、フォルダ構成を工夫すればテスト観点ごとにサブフォルダで分けることもできます。
users-api/
├── bruno.json
├── 01_create_user.bru
├── 02_get_users.bru
└── 03_delete_user.bru
ファイル名の01_、02_といった連番は、実行順序を保証するものではなく、あくまで人間が見たときにフォルダ内で意図した並びになるようにするための命名慣習です。実際の実行順序を決めているのは、各.bruファイルのmetaブロックにあるseqという数値フィールドです。
meta {
name: Create User
type: http
seq: 1
}
Bruno CLI(bru run)はデフォルトでランダム実行ではなく逐次実行され、このseqの昇順(GUIのサイドバーで並んでいる順序と同じ)でリクエストが実行されます。GUI上でドラッグ&ドロップして順番を入れ替えると、このseqの値も自動的に更新されます。ファイル名の連番とseqの値がズレると紛らわしいので、ファイルをリネームしたり並び替えたりした際は、GUIで一度開いてseqとファイル名が一致しているか確認しておくと安心です。
4. Environmentで環境を切り替える
開発環境・検証環境・本番環境など、APIのURLや認証情報は環境ごとに異なることがほとんどです。Brunoの「Environment」機能を使うと、環境ごとに変数を定義しておき、リクエスト側では変数名だけを記述できます。
url: {{base_url}}/users
base_urlという変数に開発環境用・本番環境用のドメインをそれぞれ登録しておけば、画面上部のEnvironmentを切り替えるだけで、同じリクエストを別の環境に送信できます。
vars {
base_url: https://staging.example.com/api
}
APIキーなどの機密情報は「Secret」としてマークすることで、実行結果やログに値が露出しないようになっています。
ハマったポイント:Brunoは「Secret」に指定した値を、実行レポート中の全出現箇所で伏せ字にします。これは意図通りの挙動なのですが、
client_id=1のような短い値や、他の設定と共通する文字列をSecretに入れてしまうと、ログ中の無関係な数字や文字列まで巻き込んで********になり、レポートが読みにくくなることがありました。本当に秘密にすべき値(トークンやパスワードなど)だけをSecretに登録し、それ以外の設定値はEnvironmentの通常の変数として分けておくのがおすすめです。
5. Testsでレスポンスを検証する
ここがPostmanとの一番の違いであり、テスト自動化の要になる機能です。Brunoでは各リクエストにtestsブロックを書くことで、レスポンスの検証をコード化できます。
tests {
test("ステータスコードが200であること", function () {
expect(res.getStatus()).to.equal(200);
});
test("レスポンスに email が含まれること", function () {
expect(res.getBody()).to.have.property("email");
});
}
resオブジェクトからステータスコード・ヘッダー・レスポンスボディにアクセスでき、Chaiライクなアサーション構文(expect)がそのまま使えるので、Postmanのpm.testに慣れていればすぐに書けます。手動で「レスポンスを目で見て確認する」代わりに、このtestsブロックがそのままテストコードになる、というのがBrunoで自動化を進める第一歩です。
図: Testsタブにexpectでステータスコードの検証を書いた状態。右側のResponseで実際の返り値も確認できる
Cursorのターミナルからテストを自動実行する
まず基本として、CLIをインストールしてbru runを直接叩いてみましょう。Cursorでコレクションのフォルダを開き、Ctrl+@(Cmd+J)で統合ターミナルを開きます。
npm install -g @usebruno/cli
bru run
図: npm install -g @usebruno/cliでCLIを入れたあとbru runを実行。StatusがPASS、リクエストが1件成功していることがターミナル上で確認できる
慣れないうちはこのように直接bru runを叩いて問題ありませんが、実務では素のCLIコマンドをそのまま叩くというより、チームで用意した実行用スクリプト(./scripts/run-tests.shのようなラッパー)をCursorのターミナルから呼び出す形に落ち着くケースが多いです。実際に運用してみると、bru runのオプション(環境指定・レポート出力形式など)をメンバー全員が覚えるのは現実的ではなく、スクリプト側にデフォルト値をまとめておいた方がミスなく実行できました。オプションを毎回指定する必要がなく、実行対象の指定方法だけ覚えればいいので、Bruno自体に詳しくないメンバーでもテストを回せるようになります。
普段のコーディングをCursorで行っている場合、Bruno GUIアプリを別途開かなくても、エディタと同じウィンドウ内でAPIテストを回せます。コンテキストスイッチが減り、テスト駆動での開発サイクルが組みやすくなるのが利点です。
たとえば、対象の機能名を引数に渡して実行する、というようなインターフェースにしておくと、テスト観点ごとに絞り込んで確認できて便利です。
./scripts/run-tests.sh <機能名>
run-tests.shの中身は、最小構成であれば以下のようになります。引数で受け取った対象をbru runに渡し、--reporter-junitオプションでJUnit形式のレポートを出力しています。このレポートが、後述のCI設定でresults.xmlとしてアップロードしているファイルの実体です。
#!/bin/bash
set -e
# 引数省略時はコレクション全体を対象にする
TARGET="${1:-.}"
bru run "$TARGET" \
--env staging \
--reporter-junit results.xml
実運用ではここに、失敗時の通知や、対象ディレクトリのバリデーションなどを追加していく形になります。
テストが失敗した場合は、ターミナルに表示されたエラーメッセージをそのままコピーしてCursorのAIチャットに貼り付け、「このアサーションがなぜ失敗したか教えて」と聞くと、.bruファイルの該当箇所を踏まえた原因の切り分けを手伝ってもらえます。エディタとターミナル、AIチャットが同じウィンドウ内にあるCursorならではの使い方です。
CI/CDパイプラインへの組み込み
Cursorのターミナルでの実行はあくまでローカル確認用です。チーム開発では、Cursor上でテストが通ることを確認してからPRを作成し、CI上でも同じスクリプトを自動実行して品質を担保する構成が一般的です。ローカルとCIで実行コマンドをほぼ揃えられるのも、スクリプトにまとめておくことの利点です。
> 注意:以下のワークフロー例は構成の参考例であり、この記事執筆時点でCI環境上での動作を検証したものではありません。実際に導入する際は、リポジトリの環境に合わせて動作確認をお願いします。
name: API Tests with Bruno
on: [push, pull_request]
jobs:
api-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- name: Install Bruno CLI
+ run: npm install -g @usebruno/cli
- name: Run API tests
working-directory: ./bruno-collection
run: ./scripts/run-tests.sh
env:
API_TOKEN: ${{ secrets.API_TOKEN }}
- name: Upload test report
if: always()
uses: actions/upload-artifact@v4
with:
name: bruno-results
path: bruno-collection/results.xml
GitHubリポジトリの Settings → Branches で「Require status checks to pass before merging」を有効にし、このワークフローを必須チェックに指定すれば、テストが通らないPRはマージできなくなります。
削減できた工数の目安
実際に3つのテストケースで、手動でPostmanを操作して確認した場合と、Brunoでbru runを実行した場合の所要時間を比較しました。
| ケース | 手動でPostmanを操作して確認 |
bru runで自動実行 |
削減率 |
|---|---|---|---|
| ケース1 | 2時間 | 1時間 | 約50%減 |
| ケース2 | 15時間 | 2時間 | 約87%減 |
| ケース3 | 3時間 | 1.5時間 | 約50%減 |
| 合計 | 20時間 | 4.5時間 | 約78%減 |
特にケース2のようにテストケース数が多い(=手動確認の工数が大きい)対象ほど、自動化による削減効果が大きく出る傾向がありました。
検証環境
| 項目 | バージョン |
|---|---|
| Bruno(GUI) | v4.0.0 |
| @usebruno/cli | v4.0.0 |
| OS | macOS |
Brunoは開発速度が速く、
.bruの記法やCLIオプションが変わる可能性があります。異なるバージョンでは挙動が変わる場合がある点にご留意ください。
参考リンク
まとめ
今回はPostmanとの違いを踏まえながら、Brunoの基本機能とテスト自動化の第一歩を整理しました。
- BrunoはPostmanの Collection・Environment・Auth に相当する機能を、すべてプレーンテキストの
.bruファイルで管理できる -
.bruファイルはGitでそのままバージョン管理・レビューできる -
testsブロックにアサーションを書くことで、手動確認をコード化できる -
bruというCLIが標準で使えるため、Cursorのターミナルやスクリプト経由でテストを自動実行できる - ローカル(Cursorのターミナル)での実行が通ることを確認してから、GitHub ActionsなどのCI/CDパイプラインに組み込むと、手動確認にかかっていた工数を継続的に削減できる
まずは既存のAPIコレクションを1つBrunoに移し、Cursorのターミナルからテストを実行できる状態を作るところから始めてみてください。
お知らせ
技術ブログを週1〜2本更新中、ソーイをフォローして最新記事をチェック!
https://qiita.com/organizations/sewii




