Skip to main content

API Mocks

Staqr uses Microcks for sophisticated API mocking during integration testing. This guide covers how to use mock services in your development workflow.

Overview

API mocks allow you to:

  • Develop without carrier access: Build integrations before credentials are available
  • Test error scenarios: Simulate carrier errors, timeouts, and edge cases
  • Validate contracts: Ensure your code sends valid requests per WSDL/OpenAPI specs
  • Run CI/CD tests: Reliable integration tests without external dependencies

Quick Start

1. Start Mock Services

# Using Docker Compose (local development)
docker compose -f docker-compose.yml --profile mocks up -d

# Verify running
curl http://localhost:4750/api/health # Should return {"status":"UP"}

2. Import Carrier WSDL/OpenAPI

# Import Optus WSG WSDL
curl -X POST http://localhost:4750/api/artifact/upload \
-F "file=@mocks/microcks/import/optus-wsg-9.2.wsdl"

# Verify import
curl http://localhost:4750/api/services | jq '.[].name'

3. Configure Connection

# Use mock connection in development
export OPTUS_CONNECTION_ID=mock-optus-wsg

4. Run Integration Tests

pnpm test:integration

Mock Endpoints

When you import a WSDL or OpenAPI spec, Microcks creates mock endpoints:

ProtocolEndpoint PatternExample
SOAP/soap/{ServiceName}/{Version}/soap/WsgServiceOrderService/9.2
REST/rest/{APIName}/{Version}/{path}/rest/TelstraAPI/1.0/customers/{id}

Writing Integration Tests

Basic Integration Test

// packages/server/api/src/test/optus-adapter.integration.test.ts
import { describe, it, expect, beforeAll } from 'vitest'
import { optusWsgAdapter } from '../app/carrier/adapters/optus-wsg-adapter'

describe('Optus WSG Adapter', () => {
beforeAll(async () => {
// Ensure Microcks is ready
const health = await fetch('http://localhost:4750/api/health')
expect(health.ok).toBe(true)
})

it('should submit service order', async () => {
const adapter = await optusWsgAdapter('mock-optus-wsg')

const result = await adapter.submitServiceOrder({
orderType: 'NEW',
msisdn: '0412345678',
serviceId: 'SVC-001'
})

expect(result.orderId).toBeDefined()
expect(result.status).toBe('SUBMITTED')
})
})

Testing Error Scenarios

Configure Microcks to return different responses based on headers:

it('should handle throttle error', async () => {
const adapter = await optusWsgAdapter('mock-optus-wsg')

const result = await adapter.submitServiceOrder(
{ orderType: 'NEW', msisdn: '0412345678' },
{ headers: { 'X-Test-Scenario': 'throttle' } }
)

expect(result.error).toBe('RATE_LIMIT_EXCEEDED')
expect(result.retryAfter).toBeDefined()
})

CI/CD Integration

GitLab CI Configuration

Mock services run as service containers in GitLab CI:

integration-tests:
stage: test
image: node:20-alpine
services:
- name: quay.io/microcks/microcks:1.9.0
alias: microcks
variables:
SPRING_PROFILES_ACTIVE: uber
KEYCLOAK_ENABLED: "false"
variables:
MICROCKS_URL: http://microcks:8080
OPTUS_CONNECTION_ID: mock-optus-wsg
before_script:
- apk add --no-cache curl
- pnpm install --frozen-lockfile
# Wait for Microcks
- |
for i in $(seq 1 30); do
curl -s ${MICROCKS_URL}/api/health | grep -q "UP" && break
sleep 2
done
# Import mocks
- |
curl -X POST ${MICROCKS_URL}/api/artifact/upload \
-F "file=@mocks/microcks/import/optus-wsg-9.2.wsdl"
script:
- pnpm test:integration

Jest/Vitest Configuration

// vitest.integration.config.ts
import { defineConfig } from 'vitest/config'

export default defineConfig({
test: {
include: ['**/*.integration.test.ts'],
globalSetup: './src/test/setup-microcks.ts',
testTimeout: 30000,
}
})
// src/test/setup-microcks.ts
export async function setup() {
const MICROCKS_URL = process.env.MICROCKS_URL || 'http://localhost:4750'

// Wait for Microcks
for (let i = 0; i < 30; i++) {
try {
const res = await fetch(`${MICROCKS_URL}/api/health`)
if (res.ok) break
} catch (e) {
await new Promise(r => setTimeout(r, 2000))
}
}

// Import mock definitions
const mockFiles = ['mocks/microcks/import/optus-wsg-9.2.wsdl']
for (const file of mockFiles) {
const formData = new FormData()
formData.append('file', await Bun.file(file))
await fetch(`${MICROCKS_URL}/api/artifact/upload`, {
method: 'POST',
body: formData
})
}
}

Contract Testing

Microcks can validate that your adapter sends correct requests:

# Run contract test against your adapter
curl -X POST http://localhost:4750/api/tests \
-H "Content-Type: application/json" \
-d '{
"serviceId": "WsgServiceOrderService:9.2",
"testEndpoint": "http://host.docker.internal:4600/api/v1/carrier/optus-wsg",
"runnerType": "SOAP_HTTP"
}'

Response indicates PASS/FAIL with validation details:

{
"success": true,
"testNumber": 1,
"testCaseResults": [
{
"operationName": "SubmitServiceOrder",
"success": true
}
]
}

Directory Structure

mocks/
├── microcks/
│ └── import/
│ ├── optus-wsg-9.2.wsdl # Optus WSG SOAP contract
│ └── telstra-api-1.0.yaml # Telstra REST contract
└── mockoon/
└── carrier-mocks.json # Simple REST mocks

Best Practices

1. Version Mock Definitions

git add mocks/microcks/import/
git commit -m "feat: add Optus WSG v9.2 mock definition"

2. Use Realistic Data

{
"msisdn": "0412345678", // Valid AU mobile
"abn": "51824753556", // Valid ABN
"postcode": "2000" // Valid postcode
}

3. Document Scenarios

# mocks/README.md

## Optus WSG Mock Scenarios

| Header | Response |
|--------|----------|
| X-Test-Scenario: throttle | 503 Rate Limited |
| X-Test-Scenario: timeout | 30s delay |
| X-Test-Scenario: invalid-msisdn | 400 Validation Error |

4. Same Mocks in CI

Ensure CI uses the same mock definitions as local development:

before_script:
- curl -X POST ${MICROCKS_URL}/api/artifact/upload \
-F "file=@mocks/microcks/import/optus-wsg-9.2.wsdl"

Troubleshooting

See Mock Services Operations Guide for troubleshooting common issues.