Skip to content

API Testing Example ​

Learn how to use HTTPTests to test a REST API with full CRUD operations, status code validation, and response header verification.

Overview ​

This example demonstrates testing a REST API that provides:

  • Health check endpoint
  • User management (GET, POST, PUT, DELETE)
  • Product listing
  • JSON response validation
  • HTTP status code validation

Use Case ​

Perfect for:

  • REST API testing
  • Backend service validation
  • API contract testing
  • Regression testing after API changes
  • CI/CD integration for API projects

API Endpoints ​

MethodPathDescriptionExpected Status
GET/healthHealth check200
GET/api/usersList all users200
GET/api/users/123Get specific user200
POST/api/usersCreate new user201
PUT/api/users/123Update user200
DELETE/api/users/123Delete user204
GET/api/productsList products200

File Structure ​

api-testing/
├── Dockerfile
├── nginx.conf
└── .httptests/
    ├── config.yml
    └── test.json

Configuration Files ​

Dockerfile ​

dockerfile
FROM nginx:alpine
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Simple Nginx container that serves our API.

nginx.conf ​

nginx
events {
    worker_connections 1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    server {
        listen       80;
        server_name  _;

        # Health check endpoint
        location = /health {
            default_type application/json;
            return 200 '{"status": "healthy", "timestamp": "2025-01-01T00:00:00Z"}';
        }

        # Get all users
        location = /api/users {
            default_type application/json;
            if ($request_method = GET) {
                return 200 '{"users": [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]}';
            }
            if ($request_method = POST) {
                return 201 '{"id": 3, "name": "Charlie", "created": true}';
            }
            return 405;
        }

        # Get specific user
        location ~ ^/api/users/(\d+)$ {
            default_type application/json;
            if ($request_method = GET) {
                return 200 '{"id": $1, "name": "User $1", "email": "user$1@example.com"}';
            }
            if ($request_method = PUT) {
                return 200 '{"id": $1, "name": "Updated User", "updated": true}';
            }
            if ($request_method = DELETE) {
                return 204;
            }
            return 405;
        }

        # Get products
        location = /api/products {
            default_type application/json;
            return 200 '{"products": [{"id": 1, "name": "Widget", "price": 19.99}]}';
        }

        # Default - not found
        location / {
            default_type application/json;
            return 404 '{"error": "Not found"}';
        }
    }
}

This configuration:

  • Returns JSON responses directly from Nginx (mocking an API)
  • Handles different HTTP methods
  • Returns appropriate status codes
  • Sets correct Content-Type headers

.httptests/config.yml ​

yaml
mock:
  network_aliases:
    - backend

nginx:
  environment:
    API_VERSION: v1

Simple configuration:

  • Creates a mock service (not used in this example, but available)
  • Sets an environment variable for the API

.httptests/test.json ​

json
{
    "hosts": {
        "api.example.com": [
            {
                "paths": ["/health"],
                "method": "GET",
                "expectedStatus": 200,
                "expectedResponseHeaders": [
                    ["Content-Type", "application/json"]
                ]
            },
            {
                "paths": ["/api/users"],
                "method": "GET",
                "expectedStatus": 200,
                "expectedResponseHeaders": [
                    ["Content-Type", "application/json"]
                ]
            },
            {
                "paths": ["/api/users/123"],
                "method": "GET",
                "expectedStatus": 200,
                "expectedResponseHeaders": [
                    ["Content-Type", "application/json"]
                ]
            },
            {
                "paths": ["/api/users"],
                "method": "POST",
                "expectedStatus": 201,
                "expectedResponseHeaders": [
                    ["Content-Type", "application/json"]
                ]
            },
            {
                "paths": ["/api/users/456"],
                "method": "PUT",
                "expectedStatus": 200,
                "expectedResponseHeaders": [
                    ["Content-Type", "application/json"]
                ]
            },
            {
                "paths": ["/api/users/789"],
                "method": "DELETE",
                "expectedStatus": 204
            },
            {
                "paths": ["/api/products"],
                "method": "GET",
                "expectedStatus": 200,
                "expectedResponseHeaders": [
                    ["Content-Type", "application/json"]
                ]
            }
        ]
    }
}

This test suite validates:

  • ✅ All HTTP methods (GET, POST, PUT, DELETE)
  • ✅ Different status codes (200, 201, 204)
  • ✅ Response Content-Type headers
  • ✅ Specific path parameters

GitHub Actions Workflow ​

Add this to .github/workflows/test.yml:

yaml
name: API Tests
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: serviceguards-com/httptests-action@latest
        with:
          httptests-directory: ./api-testing

Expected Test Output ​

🔧 Adding X-Upstream-Target headers to nginx configs...
🚀 Starting environment...
🧪 Running tests for httptests-api-testing

test_endpoints (__main__.IntegrationTests.test_endpoints) ... ok
  → Testing: GET api.example.com/health
    ✓ Status code: 200 (expected 200)
    ✓ Response header: content-type = application/json
  → Testing: GET api.example.com/api/users
    ✓ Status code: 200 (expected 200)
    ✓ Response header: content-type = application/json
  → Testing: GET api.example.com/api/users/123
    ✓ Status code: 200 (expected 200)
    ✓ Response header: content-type = application/json
  → Testing: POST api.example.com/api/users
    ✓ Status code: 201 (expected 201)
    ✓ Response header: content-type = application/json
  → Testing: PUT api.example.com/api/users/456
    ✓ Status code: 200 (expected 200)
    ✓ Response header: content-type = application/json
  → Testing: DELETE api.example.com/api/users/789
    ✓ Status code: 204 (expected 204)
  → Testing: GET api.example.com/api/products
    ✓ Status code: 200 (expected 200)
    ✓ Response header: content-type = application/json
============================================================
Total assertions passed: 13
============================================================

What's Being Tested? ​

Health Check ​

Validates that the health endpoint returns 200 and JSON content type.

List Users (GET) ​

Tests retrieving a list of all users with proper JSON response.

Get User (GET) ​

Tests retrieving a specific user by ID.

Create User (POST) ​

Validates that POST returns 201 (Created) status code.

Update User (PUT) ​

Tests updating a user returns 200 status.

Delete User (DELETE) ​

Validates DELETE returns 204 (No Content).

List Products (GET) ​

Tests product listing endpoint.

Extending This Example ​

Add Request Body Validation ​

For real APIs that accept request bodies, you can add:

json
{
    "paths": ["/api/users"],
    "method": "POST",
    "requestBody": {
        "name": "John Doe",
        "email": "john@example.com"
    },
    "expectedStatus": 201
}

Add More Headers ​

Test additional response headers:

json
{
    "paths": ["/api/users"],
    "method": "GET",
    "expectedStatus": 200,
    "expectedResponseHeaders": [
        ["Content-Type", "application/json"],
        ["X-Total-Count"],
        ["X-Page"],
        ["Cache-Control", "no-cache"]
    ]
}

Test Error Cases ​

Add tests for error scenarios:

json
{
    "paths": ["/api/users/invalid"],
    "method": "GET",
    "expectedStatus": 404,
    "expectedResponseHeaders": [
        ["Content-Type", "application/json"]
    ]
}

Test Authentication ​

Add API key testing:

json
{
    "paths": ["/api/users"],
    "method": "GET",
    "expectedStatus": 401,
    "comment": "No auth = 401"
},
{
    "paths": ["/api/users"],
    "method": "GET",
    "additionalRequestHeaders": {
        "Authorization": "Bearer valid-token"
    },
    "expectedStatus": 200
}

Next Steps ​

Released under the MIT License.