QuadrastackQuadrastack
Documentation

Docs

Mock Server Setup Tutorial

Learn how to build and configure mock API servers with Quadrastack for local development and testing without external dependencies.

Mock Server Setup Tutorial

Learn how to create mock API servers directly in your test playbooks. No external tools required - everything is defined in YAML.

Time: 20 minutes | Level: Intermediate

What You'll Build

A complete mock API server with:

  • Multiple endpoints (GET, POST, PUT, DELETE)
  • Path parameters and dynamic routing
  • Request matching (headers, body, query params)
  • Response sequences
  • Multiple mock servers running simultaneously

Prerequisites

  • Completed First API Test tutorial
  • Understanding of REST APIs
  • 20 minutes

Why Use Mock Servers?

Mock servers are essential for:

  • Local Development - Test without internet or real APIs
  • Controlled Testing - Simulate specific scenarios (errors, edge cases)
  • Faster Tests - No network latency
  • Team Collaboration - Share reproducible test environments
  • Offline Work - Develop and test anywhere

Step 1: Create Your First Mock Server

Create a new directory:

mkdir mock-server-tutorial
cd mock-server-tutorial

Create mocks.yaml:

mockServer:
  my-api:
    description: My first mock API
    listen: ":8080"

mocks:
  hello-world:
    server: my-api
    description: Simple hello world endpoint
    match:
      method: GET
      path: /hello
    respond:
      - status: 200
        body: |
          {
            "message": "Hello, World!"
          }

Start the mock server:

quadrastack --mock-server my-api

Expected Output:

Mock server 'my-api' started on :8080
Registered routes:
  GET /hello
Press Ctrl+C to stop

Test it with curl:

curl http://localhost:8080/hello

Response:

{
  "message": "Hello, World!"
}

Step 2: Add Multiple HTTP Methods

Expand mocks.yaml to support CRUD operations:

mockServer:
  my-api:
    description: CRUD API mock server
    listen: ":8080"
    defaults:
      status: 200
      headers:
        Content-Type: application/json

mocks:
  list-users:
    server: my-api
    description: Get all users
    match:
      method: GET
      path: /api/users
    respond:
      - body: |
          [
            {
              "id": "1",
              "name": "Alice Johnson",
              "email": "alice@example.com"
            },
            {
              "id": "2",
              "name": "Bob Smith",
              "email": "bob@example.com"
            }
          ]

  create-user:
    server: my-api
    description: Create new user
    match:
      method: POST
      path: /api/users
    respond:
      - status: 201
        body: |
          {
            "id": "3",
            "name": "New User",
            "email": "new@example.com",
            "createdAt": "2024-01-01T00:00:00Z"
          }

  get-user:
    server: my-api
    description: Get single user by ID (Dynamic Response)
    match:
      method: GET
      path: /api/users/{id}
    respond:
      - body: |
          {
            "id": "{id}",
            "name": "User {id}",
            "email": "user{id}@example.com"
          }

  update-user:
    server: my-api
    description: Update existing user
    match:
      method: PUT
      path: /api/users/{id}
    respond:
      - body: |
          {
            "id": "{id}",
            "name": "Updated User",
            "email": "updated@example.com",
            "updatedAt": "2024-01-01T00:00:00Z"
          }

  delete-user:
    server: my-api
    description: Delete user
    match:
      method: DELETE
      path: /api/users/{id}
    respond:
      - status: 204

Restart your mock server and test:

# List users
curl http://localhost:8080/api/users

# Get specific user (notice {id} is replaced)
curl http://localhost:8080/api/users/42

# Create user
curl -X POST http://localhost:8080/api/users \
  -H "Content-Type: application/json" \
  -d '{"name":"Jane","email":"jane@example.com"}'

# Update user
curl -X PUT http://localhost:8080/api/users/1 \
  -H "Content-Type: application/json" \
  -d '{"name":"Updated Jane"}'

# Delete user
curl -X DELETE http://localhost:8080/api/users/1

Step 3: Advanced Request Matching

Match requests based on headers, query parameters, and body content:

mocks:
  auth-required:
    server: my-api
    description: Endpoint requiring authentication
    match:
      method: GET
      path: /api/protected
      headers:
        Authorization: "Bearer secret-token"
    respond:
      - body: |
          {
            "message": "Access granted",
            "user": "authenticated-user"
          }

  auth-missing:
    server: my-api
    description: Handle missing authentication
    match:
      method: GET
      path: /api/protected
    respond:
      - status: 401
        body: |
          {
            "error": "Unauthorized",
            "message": "Authentication required"
          }

  search-users:
    server: my-api
    description: Search with query parameters
    match:
      method: GET
      path: /api/users/search
      query:
        role: "admin"
    respond:
      - body: |
          [
            {
              "id": "1",
              "name": "Admin User",
              "role": "admin"
            }
          ]

  create-with-validation:
    server: my-api
    description: Validate request body fields
    match:
      method: POST
      path: /api/users
      body:
        $.email: contains "@"
    respond:
      - status: 201
        body: |
          {
            "id": "new-id",
            "message": "User created with valid email"
          }

  create-invalid-email:
    server: my-api
    description: Reject invalid email
    match:
      method: POST
      path: /api/users
    respond:
      - status: 400
        body: |
          {
            "error": "Invalid email format"
          }

Test the matching:

# Success with token
curl http://localhost:8080/api/protected \
  -H "Authorization: Bearer secret-token"

# Fail without token
curl http://localhost:8080/api/protected

# Search with query
curl "http://localhost:8080/api/users/search?role=admin"

Step 4: Response Sequences

Simulate changing state with response sequences:

mocks:
  polling-endpoint:
    server: my-api
    description: Endpoint that changes over time
    match:
      method: GET
      path: /api/job/status
    respond:
      - status: 200
        body: |
          {
            "status": "pending",
            "progress": 0
          }
        count: 2

      - status: 200
        body: |
          {
            "status": "processing",
            "progress": 50
          }
        count: 3

      - status: 200
        body: |
          {
            "status": "completed",
            "progress": 100
          }

  rate-limited:
    server: my-api
    description: Simulate rate limiting
    match:
      method: GET
      path: /api/limited
    respond:
      - status: 200
        body: |
          {
            "message": "Request 1 OK"
          }
        count: 3

      - status: 429
        body: |
          {
            "error": "Too Many Requests",
            "retryAfter": 60
          }

Test the sequence:

# First 2 calls return "pending"
curl http://localhost:8080/api/job/status
curl http://localhost:8080/api/job/status

# Next 3 calls return "processing"
curl http://localhost:8080/api/job/status
curl http://localhost:8080/api/job/status
curl http://localhost:8080/api/job/status

# All subsequent calls return "completed"
curl http://localhost:8080/api/job/status

Step 5: Multiple Mock Servers

Run multiple mock servers simultaneously:

mockServer:
  user-service:
    description: User management service
    listen: ":8080"
    defaults:
      headers:
        Content-Type: application/json

  payment-service:
    description: Payment processing service
    listen: ":8081"
    defaults:
      headers:
        Content-Type: application/json

  notification-service:
    description: Notification service
    listen: ":8082"
    defaults:
      headers:
        Content-Type: application/json

mocks:
  get-user:
    server: user-service
    match:
      method: GET
      path: /users/{id}
    respond:
      - body: |
          {
            "id": "{id}",
            "name": "User {id}"
          }

  process-payment:
    server: payment-service
    match:
      method: POST
      path: /payments
    respond:
      - status: 201
        body: |
          {
            "paymentId": "pay-12345",
            "status": "success"
          }

  send-notification:
    server: notification-service
    match:
      method: POST
      path: /notifications
    respond:
      - status: 202
        body: |
          {
            "notificationId": "notif-67890",
            "status": "queued"
          }

Start all servers:

quadrastack --mock-server user-service --mock-server payment-service --mock-server notification-service

Test each service:

# User service on port 8080
curl http://localhost:8080/users/123

# Payment service on port 8081
curl -X POST http://localhost:8081/payments

# Notification service on port 8082
curl -X POST http://localhost:8082/notifications

Step 6: Using Base Paths

Organize your API with base paths:

mockServer:
  versioned-api:
    description: API with version prefix
    listen: ":8080"
    basePath: /api/v1

mocks:
  v1-users:
    server: versioned-api
    description: Version 1 users endpoint
    match:
      method: GET
      path: /users
    respond:
      - body: |
          {
            "version": "v1",
            "users": []
          }

Access with base path:

# basePath is automatically prepended
curl http://localhost:8080/api/v1/users

Step 7: Testing Your Mocks

Create requests.yaml to test your mock server:

requests:
  test-list-users:
    method: GET
    url: http://localhost:8080/api/users
    expect:
      status: 200
      body:
        $: type array
        $.length: "> 0"
        $[0].id: exists

  test-create-user:
    method: POST
    url: http://localhost:8080/api/users
    headers:
      Content-Type: application/json
    body:
      name: "Test User"
      email: "test@example.com"
    expect:
      status: 201
      body:
        $.id: exists
        $.createdAt: exists

  test-auth-protected:
    method: GET
    url: http://localhost:8080/api/protected
    headers:
      Authorization: "Bearer secret-token"
    expect:
      status: 200
      body:
        $.message: "Access granted"

  test-auth-denied:
    method: GET
    url: http://localhost:8080/api/protected
    expect:
      status: 401

In one terminal, start the mock server:

quadrastack --mock-server my-api

In another terminal, run the tests:

quadrastack

Step 8: Error Simulation

Create realistic error scenarios:

mocks:
  server-error:
    server: my-api
    description: Simulate 500 error
    match:
      method: GET
      path: /api/error/500
    respond:
      - status: 500
        body: |
          {
            "error": "Internal Server Error",
            "timestamp": "2024-01-01T00:00:00Z"
          }

  not-found:
    server: my-api
    description: Simulate 404 error
    match:
      method: GET
      path: /api/users/999
    respond:
      - status: 404
        body: |
          {
            "error": "User not found",
            "userId": "999"
          }

  timeout-simulation:
    server: my-api
    description: Slow response
    match:
      method: GET
      path: /api/slow
    respond:
      - status: 200
        body: |
          {
            "message": "Slow response"
          }
        delay: "5s"

Final Project Structure

mock-server-tutorial/
├── mocks.yaml
└── requests.yaml

Your mocks.yaml should now contain all the mock definitions created in the previous steps.

What You Learned

  • Creating mock servers with mockServer
  • Defining mock endpoints with mocks
  • HTTP method matching (GET, POST, PUT, DELETE)
  • Path parameters with {paramName}
  • Advanced matching (headers, query, body)
  • Response sequences with count
  • Running multiple mock servers
  • Using base paths
  • Dynamic responses
  • Error simulation

Best Practices

  1. Use Descriptive Names - Clear mock names help with debugging
  2. Add Descriptions - Document what each mock does
  3. Set Defaults - Use server defaults for common headers
  4. Match Order Matters - More specific matches should come first
  5. Test Your Mocks - Write tests against your mock endpoints
  6. Simulate Reality - Include realistic errors and edge cases
  7. Use Sequences - Model changing state over time
  8. Organize by Service - Use multiple servers for microservices

Next Steps

Common Issues

Port Already in Use

If port 8080 is busy, choose a different port:

mockServer:
  my-api:
    listen: ":8888"  # Use different port

Mock Not Matching

Use verbose mode to see why requests don't match:

quadrastack --mock-server my-api --verbose

Response Not Updating

Remember sequences reset when the server restarts. Each count is per server instance.

Advanced Tips

  1. JSON Formatting - Use | for multiline JSON bodies
  2. Path Parameters - Use {param} and reference with {param} in response
  3. Exact vs Flexible - Use exact matches for critical fields, flexible for others
  4. Development Workflow - Keep mock server running, edit YAML, test immediately
  5. Share Mocks - Commit mocks to git for team collaboration

Congratulations! You can now build sophisticated mock API servers for testing and development.

Mock Server Setup Tutorial - Quadrastack Docs