skills/bruno-api-testing/SKILL.md
Create, run, and maintain API test collections using Bruno (OpenCollection YAML format and legacy Bru format). Use when the user wants to: (1) create a Bruno API test collection from scratch or from OpenAPI/Swagger specs, (2) write API request files with tests and assertions, (3) run API tests using bru CLI, (4) generate test reports (HTML, JUnit, JSON), (5) set up CI/CD pipelines (GitHub Actions) for automated API testing, (6) debug or fix failing Bruno API tests, (7) add environment configurations for API testing, (8) chain API requests with data extraction, or (9) work with any .yml/.bru Bruno collection files. Triggers on mentions of 'Bruno', 'bru CLI', 'API testing collection', 'OpenCollection', or requests to automate API testing with file-based collections.
npx skillsauth add jim60105/copilot-prompt bruno-api-testingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Create and run API test collections using Bruno — a Git-first, offline-only API client that stores collections as plain files.
Bruno supports two file formats. Determine which to use:
.yml files and opencollection.yml root. Preferred for new projects..bru files and bruno.json root. 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.
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.
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
]
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.
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}}.
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.
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.
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.
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.
opencollection.yml (YAML) or bruno.json (Bru) at collection rootmeta: instead of info: in YAML request filestest instead of tests (plural)http:, method:) in opencollection.ymlworking-directory in CI/CD stepssecret: true in env files + CI/CD secrets|- for body data is required in YAML to preserve JSON formattingseq number in info: — controls execution orderfolder.yml script/auth inheritance in CLI — Bruno's Sandwich execution order (Collection → Folder → Request) for before-request scripts may work in the Bruno GUI, but @usebruno/cli does NOT reliably inherit folder.yml scripts or auth settings to individual test files. Always add before-request scripts and auth blocks directly to each request file that needs them. The folder.yml is useful for documentation and GUI users, but CLI-driven tests must be self-contained.Bruno supports two script flows:
before-request → Folder before-request → Request before-request → Request is sent → Request after-response → Folder after-response → Collection after-responsebefore-request → Folder before-request → Request before-request → Request is sent → Collection after-response → Folder after-response → Request after-responseRequest assertions and request tests run after the post-response scripts.
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.
bru.setVar())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.
.bru file format referencereq, res, bru object API with runner control, cookies, utilitiesdevelopment
Diátaxis Documentation Expert. An expert technical writer specializing in creating high-quality software documentation, guided by the principles and structure of the Diátaxis technical documentation authoring framework.
testing
Guide users through a structured workflow for co-authoring documentation. Use when user wants to write documentation, proposals, technical specs, decision docs, or similar structured content. This workflow helps users efficiently transfer context, refine content through iteration, and verify the doc works for readers. Trigger when user mentions writing docs, creating proposals, drafting specs, or similar documentation tasks.
tools
Comprehensive guide for building, configuring, customizing, and deploying Docsify documentation sites. Use when the user wants to (1) initialize a new Docsify site, (2) add or organize Markdown pages, sidebars, navbars, or cover pages, (3) configure `window.$docsify` options, (4) customize themes / CSS variables / fonts, (5) install built-in or third-party Docsify plugins (search, GA, emoji, zoom, copy-code, comments, pagination, tabs, etc.), (6) write a custom Docsify plugin using lifecycle hooks, (7) use Docsify Markdown helpers (callouts, link attributes, image attributes, heading IDs, task lists, embed files with `:include`), (8) deploy to GitHub Pages, GitLab Pages, Netlify, Vercel, Firebase, Docker, Nginx, etc., (9) enable PWA / offline mode, virtual routes, or Vue compatibility, or (10) upgrade a Docsify site from v4 to v5. Triggers on mentions of "docsify", "_sidebar.md", "_navbar.md", "_coverpage.md", "$docsify", or `docsify-cli`.
testing
Writing guidelines for producing high-quality Traditional Chinese (zh-TW) content. Use when writing any kind of content. Including blog posts, notes, technical articles, technical writing, chitchat, social media posts, etc., even when you are just sending a text message. Also use when reviewing or editing existing Chinese content for tone, style, and terminology compliance.