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@v2
        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.