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

  1. Download Postman from postman.com/downloads
  2. Install and sign in (create free account)
  3. 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:

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 action
  • GET List Users (with filters) - Specify variant
  • POST 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.md for API design