| name | api-testing |
| description | Use for testing HTTP endpoints directly using cross-platform API testing tools. Covers .http files (VS Code REST Client, JetBrains HTTP Client), Bruno collections, Postman/Newman, k6 for functional API verification, environment management, and CI integration patterns.
USE FOR: .http files, Bruno, Postman, Newman, REST Client, k6 API tests, HTTP endpoint testing, API collections, environment variables, API functional verification, HTTP request scripting, API test automation
DO NOT USE FOR: browser-based testing (use e2e-testing), contract verification between services (use contract-testing), load/stress testing at scale (use performance-testing)
|
| license | MIT |
| metadata | {"displayName":"API Testing","author":"Tyler-R-Kendrick"} |
| compatibility | claude, copilot, cursor |
| references | [{"title":"Bruno Documentation","url":"https://docs.usebruno.com"},{"title":"Postman Documentation","url":"https://learning.postman.com/docs/getting-started/introduction/"},{"title":"k6 Documentation","url":"https://grafana.com/docs/k6/latest/"}] |
API Testing
Overview
API testing verifies HTTP endpoints directly without a browser or UI layer. It validates request/response contracts, status codes, headers, payloads, authentication, and error handling at the transport level. API tests are faster than E2E tests and provide high confidence that your services behave correctly.
Cross-Platform Tools
| Tool | Type | Strengths |
|---|
| .http files | VS Code REST Client / JetBrains HTTP Client | Version-controlled, inline in IDE, variable support |
| Bruno | Desktop + CLI | Git-friendly .bru collections, open source, no cloud sync required |
| Postman / Newman | Desktop + CLI | Rich GUI, team collaboration, extensive scripting, CI via Newman |
| k6 | CLI (JavaScript) | Scripted API tests with checks and thresholds, doubles as load tool |
| REST Client (VS Code) | VS Code extension | Lightweight, .http/.rest files, inline responses |
.http Files (VS Code REST Client / JetBrains HTTP Client)
Syntax Overview
The .http file format is supported by both VS Code REST Client and JetBrains HTTP Client with minor differences. Requests are separated by ###.
Example: requests.http
### Variables
@baseUrl = https://api.example.com
@authToken = Bearer {{$dotenv AUTH_TOKEN}}
### GET — List users
GET {{baseUrl}}/users?page=1&limit=10
Authorization: {{authToken}}
Accept: application/json
### POST — Create a user
POST {{baseUrl}}/users
Authorization: {{authToken}}
Content-Type: application/json
{
"name": "Jane Doe",
"email": "jane@example.com",
"role": "admin"
}
### PUT — Update user
PUT {{baseUrl}}/users/42
Authorization: {{authToken}}
Content-Type: application/json
{
"name": "Jane Smith"
}
### DELETE — Remove user
DELETE {{baseUrl}}/users/42
Authorization: {{authToken}}
Environment Files
Create environment-specific variable files for VS Code REST Client:
{
"rest-client.environmentVariables": {
"$shared": {
"version": "v1"
},
"development": {
"baseUrl": "http://localhost:3000",
"authToken": "dev-token-123"
},
"staging": {
"baseUrl": "https://staging-api.example.com",
"authToken": "staging-token-456"
},
"production": {
"baseUrl": "https://api.example.com",
"authToken": "prod-token-789"
}
}
}
JetBrains HTTP Client Environment
{
"development": {
"baseUrl": "http://localhost:3000",
"authToken": "dev-token-123"
},
"staging": {
"baseUrl": "https://staging-api.example.com",
"authToken": "staging-token-456"
}
}
{
"development": {
"authToken": "real-dev-secret"
}
}
Bruno
Overview
Bruno is an open-source, git-friendly API client. Collections are stored as plain-text .bru files that can be version-controlled alongside your code.
Example: .bru File Format
// collection/users/get-users.bru
meta {
name: Get Users
type: http
seq: 1
}
get {
url: {{baseUrl}}/users?page=1&limit=10
body: none
auth: bearer
}
auth:bearer {
token: {{authToken}}
}
headers {
Accept: application/json
}
assert {
res.status: eq 200
res.body.length: gt 0
}
script:post-response {
const data = res.getBody();
bru.setVar("firstUserId", data[0].id);
}
// collection/users/create-user.bru
meta {
name: Create User
type: http
seq: 2
}
post {
url: {{baseUrl}}/users
body: json
auth: bearer
}
auth:bearer {
token: {{authToken}}
}
body:json {
{
"name": "Jane Doe",
"email": "jane@example.com",
"role": "admin"
}
}
assert {
res.status: eq 201
res.body.name: eq "Jane Doe"
}
Bruno Environment Files
// environments/development.bru
vars {
baseUrl: http://localhost:3000
authToken: dev-token-123
}
// environments/staging.bru
vars {
baseUrl: https://staging-api.example.com
authToken: staging-token-456
}
Bruno CLI Runner (CI Integration)
npm install -g @usebruno/cli
bru run --env development
bru run collection/users --env staging
bru run --env staging --output results.xml --format junit
Postman / Newman
Collection Structure
Postman collections are JSON files containing organized API requests with folders, variables, and test scripts.
Pre-Request Scripts
const timestamp = Date.now();
pm.variables.set("requestId", `req-${timestamp}`);
if (!pm.environment.get("accessToken")) {
pm.sendRequest({
url: pm.environment.get("authUrl") + "/token",
method: "POST",
header: { "Content-Type": "application/json" },
body: {
mode: "raw",
raw: JSON.stringify({
client_id: pm.environment.get("clientId"),
client_secret: pm.environment.get("clientSecret"),
grant_type: "client_credentials"
})
}
}, (err, res) => {
pm.environment.set("accessToken", res.json().access_token);
});
}
Post-Response Test Scripts
pm.test("Status code is 200", () => {
pm.response.to.have.status(200);
});
pm.test("Response has required fields", () => {
const json = pm.response.json();
pm.expect(json).to.have.property("id");
pm.expect(json).to.have.property("name");
pm.expect(json).to.have.property("email");
});
pm.test("Response time is under 500ms", () => {
pm.expect(pm.response.responseTime).to.be.below(500);
});
pm.environment.set("userId", pm.response.json().id);
Newman CLI Runner (CI Integration)
npm install -g newman
newman run collection.json \
--environment staging.postman_environment.json \
--reporters cli,junit \
--reporter-junit-export results.xml
newman run collection.json \
--environment staging.postman_environment.json \
--iteration-data test-data.csv \
--iteration-count 5
newman run collection.json \
--environment staging.postman_environment.json \
--bail
GitHub Actions Integration
name: API Tests
on: [push, pull_request]
jobs:
api-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm install -g newman newman-reporter-htmlextra
- run: |
newman run tests/api/collection.json \
--environment tests/api/env-staging.json \
--reporters cli,junit,htmlextra \
--reporter-junit-export results/junit.xml \
--reporter-htmlextra-export results/report.html
- uses: actions/upload-artifact@v4
if: always()
with:
name: api-test-results
path: results/
k6 for API Testing
Overview
k6 is typically associated with load testing, but it excels at functional API verification too. Its JavaScript-based scripting, built-in checks, and threshold system make it powerful for API testing in CI pipelines.
Functional API Test with Checks
import http from "k6/http";
import { check, group } from "k6";
const BASE_URL = __ENV.BASE_URL || "http://localhost:3000";
const AUTH_TOKEN = __ENV.AUTH_TOKEN || "dev-token";
export const options = {
vus: 1,
iterations: 1,
thresholds: {
checks: ["rate==1.0"],
http_req_duration: ["p(95)<2000"],
},
};
export default function () {
const headers = {
Authorization: `Bearer ${AUTH_TOKEN}`,
"Content-Type": "application/json",
};
group("User CRUD operations", () => {
const createRes = http.post(
`${BASE_URL}/users`,
JSON.stringify({
: ,
: ,
: ,
}),
{ headers }
);
(createRes, {
: r. === ,
: r.() !== ,
: r.() === ,
});
userId = createRes.();
getRes = http.(, { headers });
(getRes, {
: r. === ,
: r.() === userId,
});
updateRes = http.(
,
.({ : }),
{ headers }
);
(updateRes, {
: r. === ,
: r.() === ,
});
deleteRes = http.(, , { headers });
(deleteRes, {
: r. === ,
});
verifyRes = http.(, { headers });
(verifyRes, {
: r. === ,
});
});
}
Running k6 API Tests
k6 run tests/api/users.k6.js
k6 run tests/api/users.k6.js \
--env BASE_URL=https://staging-api.example.com \
--env AUTH_TOKEN=staging-token
k6 run tests/api/users.k6.js --out json=results.json
k6 run tests/api/users.k6.js --summary-export=summary.json
Environment Management Patterns
Pattern: Environment Configuration File
project/
tests/
api/
environments/
dev.env.json
staging.env.json
prod.env.json # Read-only / smoke tests only
collections/
users.http
orders.http
k6/
users.k6.js
Pattern: Shared Variables Across Tools
{
"baseUrl": "https://staging-api.example.com",
"authUrl": "https://staging-auth.example.com",
"clientId": "staging-client-id",
"timeout": 5000,
"retries": 2
}
Pattern: CI Pipeline with Multiple API Test Tools
jobs:
api-tests:
runs-on: ubuntu-latest
strategy:
matrix:
environment: [staging]
steps:
- uses: actions/checkout@v4
- run: npx httpyac tests/api/**/*.http --all --env ${{ matrix.environment }}
- run: npx @usebruno/cli run tests/api/bruno --env ${{ matrix.environment }}
- run: |
k6 run tests/api/k6/users.k6.js \
--env BASE_URL=${{ secrets.STAGING_API_URL }} \
--env AUTH_TOKEN=${{ secrets.STAGING_AUTH_TOKEN }}
Best Practices
General
- Version-control your API test collections alongside the code they test.
- Use environment files to separate configuration from test logic.
- Never commit secrets — use environment variables,
.env files (gitignored), or CI secrets.
- Assert on response structure, not just status codes — validate required fields, types, and values.
- Test error cases: 400 (bad input), 401 (unauthorized), 403 (forbidden), 404 (not found), 409 (conflict), 422 (validation), 500 (server error).
.http Files
- Use
### separators to organize requests logically in a single file.
- Use
@variable declarations at the top for shared values.
- Use
{{$dotenv VAR}} for secrets to avoid hardcoding tokens.
- Keep
.http files next to the API code they test for easy discovery.
Bruno
- Commit
.bru files directly to git — they are designed for version control.
- Use the
assert block for inline response validation.
- Use
script:post-response to chain values between requests.
- Prefer Bruno over Postman when you need offline, git-native API testing.
Postman / Newman
- Export collections as v2.1 JSON and commit them to the repository.
- Use collection-level variables for values shared across requests.
- Use
pm.test() in post-response scripts for assertions.
- Run Newman in CI with
--bail to fail fast on errors.
k6
- Use
vus: 1, iterations: 1 for functional API tests (not load testing).
- Use
check() for assertions and thresholds for pass/fail gates in CI.
- Group related requests with
group() for organized output.
- Use
__ENV for environment-specific configuration.
Environment Management
- Maintain parallel environment configs for dev, staging, and production.
- Use read-only / smoke-test-only policies for production environments.
- Centralize environment configuration in one place and generate tool-specific formats if needed.
- Document which environments are safe for write operations (POST, PUT, DELETE).