API Testing
CLI-first API testing patterns replacing GUI tools with scriptable workflows.
CLI-First API Testing
| This entry covers API testing workflows from the CLI. Postman GUI is useful for exploration, but production workflows belong in scripts and CI. |
curl as Primary Tool
Why curl over Postman for production work
curl Scriptable, versionable, runs in CI, no vendor lock-in Postman GUI exploration, team sharing, but cloud dependency httpx (Python) Programmatic, type-safe, async-capable gh api GitHub-specific, handles auth automatically
Postman Collections to curl
Export Postman collection and convert to curl
# Postman exports JSON collections
# Extract curl commands from collection
jq -r '.item[] | "# \(.name)\ncurl -X \(.request.method) \(.request.url.raw)\n"' collection.json
Convert curl to Python httpx — the useful direction
# Install curlconverter
pip install curlconverter
# Convert
curlconverter --language python \
'curl -s -u admin:pass -H "Accept: application/json" https://api.example.com/v1/data'
Environment Variables Pattern
Postman uses environments — CLI uses shell variables
# Define API environment (source from .env or gopass)
export API_BASE="https://api.example.com"
export API_TOKEN="$(gopass show -o api/example/token)"
# Use consistently
curl -s -H "Authorization: Bearer $API_TOKEN" "$API_BASE/v1/devices" | jq .
Multiple environments — function-based switching
api-prod() {
export API_BASE="https://api.example.com"
export API_TOKEN="$(gopass show -o api/prod/token)"
echo "Switched to PROD"
}
api-lab() {
export API_BASE="https://api.lab.local"
export API_TOKEN="$(gopass show -o api/lab/token)"
echo "Switched to LAB"
}
Request Collections as Shell Scripts
Organized API test script — replaces Postman collections
#!/usr/bin/env bash
set -euo pipefail
BASE="https://api.example.com"
TOKEN="${API_TOKEN:?Set API_TOKEN}"
AUTH=(-H "Authorization: Bearer $TOKEN")
JSON=(-H "Content-Type: application/json" -H "Accept: application/json")
# Health check
echo "=== Health ==="
curl -s "${AUTH[@]}" "$BASE/v1/health" | jq .
# List devices
echo "=== Devices ==="
curl -s "${AUTH[@]}" "${JSON[@]}" "$BASE/v1/devices?limit=5" | jq '.[] | {name, status}'
# Create device
echo "=== Create ==="
curl -s -X POST "${AUTH[@]}" "${JSON[@]}" "$BASE/v1/devices" \
-d '{"name": "test-device", "vlan": 99}' | jq .
ISE ERS Testing Pattern
ISE ERS test suite — verify API access and basic operations
#!/usr/bin/env bash
ISE="https://10.50.1.20:9060"
AUTH=(-u "$ISE_USER:$ISE_PASS")
HEADERS=(-H "Accept: application/json" -H "Content-Type: application/json")
echo "=== ERS Version ==="
curl -sk "${AUTH[@]}" "${HEADERS[@]}" "$ISE/ers/config/op/systemconfig/iseversion" | jq .
echo "=== Endpoint Count ==="
curl -sk "${AUTH[@]}" "${HEADERS[@]}" "$ISE/ers/config/endpoint?size=1" | jq '.SearchResult.total'
echo "=== Network Devices ==="
curl -sk "${AUTH[@]}" "${HEADERS[@]}" "$ISE/ers/config/networkdevice?size=5" | \
jq '.SearchResult.resources[] | {name, id}'
Vault API Testing Pattern
Vault health and secret access
VAULT="https://10.50.1.60:8200"
echo "=== Vault Health ==="
curl -s "$VAULT/v1/sys/health" | jq '{initialized, sealed, version}'
echo "=== KV Secret ==="
curl -s -H "X-Vault-Token: $VAULT_TOKEN" \
"$VAULT/v1/kv/data/test" | jq '.data.data'
echo "=== Token Info ==="
curl -s -H "X-Vault-Token: $VAULT_TOKEN" \
"$VAULT/v1/auth/token/lookup-self" | jq '.data | {display_name, policies, ttl}'
Response Validation
Assert expected values — API contract testing from CLI
# Test that health endpoint returns 200
status=$(curl -s -o /dev/null -w '%{http_code}' https://api.example.com/v1/health)
[[ "$status" -eq 200 ]] && echo "PASS: health" || echo "FAIL: health (got $status)"
# Test that list returns array
count=$(curl -s -H "Authorization: Bearer $TOKEN" \
https://api.example.com/v1/devices | jq 'length')
[[ "$count" -gt 0 ]] && echo "PASS: devices ($count)" || echo "FAIL: no devices"
# Test response schema has expected fields
curl -s https://api.example.com/v1/devices/1 | \
jq 'has("name", "ip", "vlan")' | grep -q true && \
echo "PASS: schema" || echo "FAIL: missing fields"
pytest for API Testing
Python API tests — domus-api pattern
import httpx
import pytest
BASE = "http://localhost:8000"
def test_health():
r = httpx.get(f"{BASE}/v1/health")
assert r.status_code == 200
assert r.json()["status"] == "ok"
def test_list_devices():
r = httpx.get(f"{BASE}/v1/devices")
assert r.status_code == 200
assert isinstance(r.json(), list)
def test_create_device():
r = httpx.post(f"{BASE}/v1/devices", json={"name": "test", "vlan": 10})
assert r.status_code == 201
assert r.json()["name"] == "test"