On this page
API Testing with Postman
This guide covers using Postman for API testing, including Postman Bot for automated testing and Newman for CI/CD integration.
Why Postman?
- Manual Testing: Quick API testing during development
- Documentation: Living API documentation with examples
- Automated Testing: Write test scripts that run automatically
- Team Collaboration: Share collections across team
- CI/CD Integration: Run tests in GitHub Actions with Newman
- Postman Bot: Auto-run tests on PRs and post results to Slack
Installation
- Download Postman from postman.com/downloads
- Install and sign in (create free account)
- Join your team workspace (ask tech lead for invitation)
Creating Collections
Collection Structure
Organize by feature/domain:
📁 Your App API
📁 Authentication
POST Login
POST Refresh Token
GET Current User
📁 Users
POST Create User
GET List Users
GET User by ID
PATCH Update User
DELETE Delete User
📁 Orders
POST Create Order
GET List Orders
GET Order by ID
PATCH Update Order Status
📁 Webhooks
POST Register Webhook
GET List Webhooks
DELETE Delete Webhook
Example Request
POST Create User:
POST {{base_url}}/api/v1/users
Content-Type: application/json
Authorization: Bearer {{token}}
{
"email": "newuser@example.com",
"full_name": "New User",
"password": "securepassword123"
}
Environment Variables
Create environments for different stages:
Local Environment
{
"base_url": "http://localhost:8000",
"token": "",
"user_id": ""
}
Staging Environment
{
"base_url": "https://staging.yourapp.com",
"token": "",
"user_id": ""
}
Production Environment (Read-Only)
{
"base_url": "https://api.yourapp.com",
"token": "{{production_token}}" // Read-only token
}
Usage:
- Switch environment in top-right dropdown
- Reference variables with
{{variable_name}}
Writing Test Scripts
Basic Test Example
// Test: Login endpoint
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});
pm.test("Response time is less than 500ms", function () {
pm.expect(pm.response.responseTime).to.be.below(500);
});
pm.test("Response has access_token", function () {
var jsonData = pm.response.json();
pm.expect(jsonData).to.have.property('access_token');
});
// Save token to environment
var jsonData = pm.response.json();
pm.environment.set("token", jsonData.access_token);
Advanced Tests
// Test: Create User
pm.test("Status code is 201", function () {
pm.response.to.have.status(201);
});
pm.test("Response matches schema", function () {
var schema = {
type: "object",
required: ["id", "email", "full_name", "created_at"],
properties: {
id: { type: "number" },
email: { type: "string" },
full_name: { type: "string" },
is_active: { type: "boolean" },
created_at: { type: "string" }
}
};
pm.response.to.have.jsonSchema(schema);
});
pm.test("Email matches request", function () {
var requestData = JSON.parse(pm.request.body.raw);
var responseData = pm.response.json();
pm.expect(responseData.email).to.eql(requestData.email);
});
// Save user_id for subsequent requests
pm.environment.set("user_id", responseData.id);
Pre-request Scripts
Automatically get fresh token before requests:
// Pre-request Script: Get Token
const loginRequest = {
url: pm.environment.get("base_url") + "/api/v1/auth/login",
method: 'POST',
header: {
'Content-Type': 'application/json',
},
body: {
mode: 'raw',
raw: JSON.stringify({
username: "admin@example.com",
password: "admin password"
})
}
};
// Only get new token if current one is expired or missing
const currentToken = pm.environment.get("token");
if (!currentToken) {
pm.sendRequest(loginRequest, function (err, response) {
if (!err && response.code === 200) {
const jsonData = response.json();
pm.environment.set("token", jsonData.access_token);
}
});
}
Common Test Patterns
Response Time
pm.test("Response time < 500ms", function () {
pm.expect(pm.response.responseTime).to.be.below(500);
});
Status Codes
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
});
pm.test("Status code is 201 Created", function () {
pm.response.to.have.status(201);
});
pm.test("Status code is 401 Unauthorized", function () {
pm.response.to.have.status(401);
});
JSON Schema Validation
pm.test("Response matches schema", function () {
const schema = {
type: "object",
required: ["id", "name"],
properties: {
id: { type: "number" },
name: { type: "string" },
price: { type: "number", minimum: 0 }
}
};
pm.response.to.have.jsonSchema(schema);
});
Array Length
pm.test("Returns 20 items", function () {
const jsonData = pm.response.json();
pm.expect(jsonData.items).to.have.lengthOf(20);
});
Custom Logic
pm.test("Price is positive", function () {
const jsonData = pm.response.json();
pm.expect(jsonData.price).to.be.above(0);
});
pm.test("Email format is valid", function () {
const jsonData = pm.response.json();
pm.expect(jsonData.email).to.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
});
Postman Bot (Slack Integration)
What is Postman Bot?
Postman Bot automatically runs your API tests and posts results to Slack:
- On PR creation: Tests run automatically
- On commits: Tests run on each push
- Results in Slack: Pass/fail posted to channel
- Early feedback: Catch API issues before merge
Setup Postman Bot
1. Publish Collection to Postman:
- Select collection → Share → Publish
- Generate publish link
2. Add Postman Bot to Slack:
- Go to Slack App Directory
- Search “Postman”
- Install Postman app to workspace
3. Connect Postman to Slack:
- In Postman: Integrations → Slack
- Authorize Slack workspace
- Select channel (e.g.,
#dev-team)
4. Configure Monitoring:
- Collection → Monitor Collection
- Set schedule: On Git push
- Connect to GitHub repository
- Select branch: main, staging, feature branches
5. Configure Notifications:
- Send to Slack:
#dev-team - Notify on: Failures and First Success
Example Slack Notification
✅ API Tests Passed
Collection: Your App API
Environment: Staging
Duration: 2.3s
Tests: 24/24 passed
Triggered by: PR #247 (feat: add webhooks)
Author: @john-doe
[View Results] [View Collection]
On failure:
❌ API Tests Failed
Collection: Your App API
Tests: 22/24 passed (2 failed)
Failed tests:
- Create User: Response time too slow (650ms)
- Get Orders: Status code 500
[View Details] [View Logs]
Newman (CI/CD Integration)
What is Newman?
Newman is the command-line runner for Postman collections. Run tests in CI/CD pipelines.
Installation
npm install -g newman
Running Collections
# Run collection
newman run collection.json -e environment.json
# Run with reporters
newman run collection.json \
-e environment.json \
--reporters cli,json \
--reporter-json-export results.json
# Run with timeout
newman run collection.json \
-e environment.json \
--timeout-request 10000
GitHub Actions Integration
Create .github/workflows/api-tests.yml:
name: API Tests
on:
push:
branches: [main, staging]
pull_request:
branches: [main, staging]
jobs:
api-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Node
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install Newman
run: npm install -g newman newman-reporter-htmlextra
- name: Wait for API to be ready
run: |
timeout 60 bash -c 'until curl -f http://localhost:8000/health; do sleep 2; done'
- name: Run Postman Collection
run: |
newman run tests/postman/collection.json \
-e tests/postman/environments/ci.json \
--reporters cli,htmlextra \
--reporter-htmlextra-export test-results.html
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v3
with:
name: api-test-results
path: test-results.html
- name: Notify Slack on Failure
if: failure()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "❌ API Tests Failed on ${{ github.ref }}",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "API tests failed. <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Details>"
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
Collection Organization
Folder Structure
📁 Your App API
📁 Setup
POST Login (get token)
📁 Users
📁 Create
POST Create User (admin)
POST Create User (non-admin, should fail)
📁 Read
GET List Users
GET User by ID
GET User by ID (not found)
📁 Update
PATCH Update User (own profile)
PATCH Update User (other user, should fail)
📁 Delete
DELETE Delete User
📁 Cleanup
DELETE Test User
Naming Convention
POST Create User- Clear actionGET List Users (with filters)- Specify variantPOST Create Order (insufficient inventory, should fail)- Expected behaviour
Best Practices
✅ Do
- Organize logically by domain/feature
- Use environment variables for base URLs, tokens
- Write descriptive tests - explain what you’re testing
- Test response time - ensure <500ms
- Validate schemas - ensure API contract is maintained
- Test error cases - 401, 403, 400, 404
- Clean up test data - delete created resources
- Share collections - commit to Git or share via Postman
❌ Don’t
- Hardcode values - use variables
- Skip error cases - test failures too
- Ignore response times - performance matters
- Leave test data - clean up after tests
- Test production APIs destructively - read-only in prod
Tips & Tricks
Generate Test Data
// Pre-request Script
const randomEmail = `test${Math.floor(Math.random() * 10000)}@example.com`;
pm.environment.set("test_email", randomEmail);
Chain Requests
// Create user, save ID, use in next request
pm.environment.set("created_user_id", pm.response.json().id);
Dynamic Headers
pm.request.headers.add({
key: 'Authorization',
value: 'Bearer ' + pm.environment.get('token')
});
Console Logging
console.log("Response:", pm.response.json());
console.log("Request:", pm.request);
Alternatives
Brief mention of alternatives:
- Insomnia: Similar to Postman, simpler UI
- HTTPie: Command-line HTTP client
- cURL: Classic command-line tool
- REST Client (VS Code extension): Test APIs from editor
Next Steps
- Create your first collection
- Add tests to all endpoints
- Set up Postman Bot in Slack
- Integrate Newman in CI/CD
- Review
../02-standards/api-patterns.mdfor API design