bruno-api-testing
Bruno API Testing
Create and run API test collections using Bruno — a Git-first, offline-only API client that stores collections as plain files.
Format Selection
Bruno supports two file formats. Determine which to use:
- YAML (OpenCollection) — Default since Bruno v3.1. Uses
.ymlfiles andopencollection.ymlroot. Preferred for new projects. - Bru (Legacy) — Uses
.brufiles andbruno.jsonroot. 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.
Workflow
1. Create Collection Structure
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:
opencollection: 1.0.0
info:
name: My API Tests
Bru format: Same structure but use bruno.json + .bru extensions. See references/bru-syntax.md.
2. Create Environment Files
YAML (environments/Local.yml):
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
YAML format — a complete request with tests:
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.
4. Chain Requests with Data Extraction
Extract data from one response and use it in subsequent requests:
YAML — Login request saving a token:
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}}.
5. Run Tests with bru CLI
Install and run:
npm install -g @usebruno/cli
# Run entire collection
cd my-api-tests && bru run --env Local
# Run specific folder
bru run Auth --env Local
# Run with developer mode (for external packages, fs access)
bru run --env Local --sandbox=developer
# Filter by tags
bru run --tags=smoke --env Local
# Generate reports
bru run --env Local \
--reporter-html results.html \
--reporter-junit results.xml \
--reporter-json results.json
# Pass secrets via CLI
bru run --env Local --env-var API_KEY=secret123
# Parallel execution
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.
6. Set Up CI/CD
See references/ci-cd.md for complete GitHub Actions workflows, matrix testing, and reporting patterns.
Minimal GitHub Actions workflow:
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.
Testing Patterns
Assertions (Declarative) — Use for Simple Checks
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.
Tests (Chai.js) — Use for Complex Validation
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.
Common Mistakes
- Missing
opencollection.yml(YAML) orbruno.json(Bru) at collection root - Using
meta:instead ofinfo:in YAML request files - Using script type
testinstead oftests(plural) - Putting request-level fields (
http:,method:) inopencollection.yml - Forgetting
working-directoryin CI/CD steps - Committing secrets — use
secret: truein env files + CI/CD secrets - Using
|-for body data is required in YAML to preserve JSON formatting - Missing
seqnumber ininfo:— controls execution order - Relying on
folder.ymlscript/auth inheritance in CLI — Bruno's Sandwich execution order (Collection → Folder → Request) forbefore-requestscripts may work in the Bruno GUI, but@usebruno/clidoes NOT reliably inheritfolder.ymlscripts or auth settings to individual test files. Always addbefore-requestscripts andauthblocks directly to each request file that needs them. Thefolder.ymlis useful for documentation and GUI users, but CLI-driven tests must be self-contained.
Script Execution Order
Bruno supports two script flows:
- Sandwich (default): Collection
before-request→ Folderbefore-request→ Requestbefore-request→ Request is sent → Requestafter-response→ Folderafter-response→ Collectionafter-response - Sequential: Collection
before-request→ Folderbefore-request→ Requestbefore-request→ Request is sent → Collectionafter-response→ Folderafter-response→ Requestafter-response
Request assertions and request tests run after the post-response scripts.
⚠️ CLI Inheritance Caveat
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:
# 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.
Variable Precedence (Highest to Lowest)
- Runtime variables (
bru.setVar()) - Request variables
- Folder variables
- Collection variables
- 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.
References
- YAML Syntax — Complete OpenCollection YAML format for requests, bodies, auth, headers, params, environments, folders, collections
- Bru Syntax — Legacy
.brufile format reference - JavaScript API — Full
req,res,bruobject API with runner control, cookies, utilities - CI/CD Integration — GitHub Actions workflows, report generation, matrix testing, environment secrets
More from jim60105/copilot-prompt
chinese-content-writing-guideline
>-
236docx
Use this skill whenever the user wants to create, read, edit, or manipulate Word documents (.docx files). Triggers include: any mention of 'Word doc', 'word document', '.docx', or requests to produce professional documents with formatting like tables of contents, headings, page numbers, or letterheads. Also use when extracting or reorganizing content from .docx files, inserting or replacing images in documents, performing find-and-replace in Word files, working with tracked changes or comments, or converting content into a polished Word document. If the user asks for a 'report', 'memo', 'letter', 'template', or similar deliverable as a Word or .docx file, use this skill. Do NOT use for PDFs, spreadsheets, Google Docs, or general coding tasks unrelated to document generation.
140pdf
Use this skill whenever the user wants to do anything with PDF files. This includes reading or extracting text/tables from PDFs, combining or merging multiple PDFs into one, splitting PDFs apart, rotating pages, adding watermarks, creating new PDFs, filling PDF forms, encrypting/decrypting PDFs, extracting images, and OCR on scanned PDFs to make them searchable. If the user mentions a .pdf file or asks to produce one, use this skill.
84rewrite-meeting-audio-transcription
Rewrite raw meeting audio transcriptions into clean, accurate meeting minutes in Traditional Chinese. Use when the user has an unprocessed audio transcription file with recognition errors and needs it cleaned up into proper meeting minutes.
26create-copilot-instructions
Create `AGENTS.md` file for a project. Use when the user wants to set up custom instructions, configure AI coding assistant behavior, or create project-specific coding guidelines for AI agents.
14drawio-diagrams-enhanced
This skill should be used when the user asks to "create a diagram", "draw a flowchart", "make a swimlane diagram", "create WBS", "generate RACI matrix", "build network diagram", "create org chart", or mentions draw.io, diagrams.net, BPMN, UML, Gantt, PERT, or project management diagrams. Integrates with next-ai-draw-io MCP server for real-time diagram creation and editing.
14