Install
openclaw skills install @zhanghengyi1986-afk/qa-api-testerAPI interface testing and automation. Send HTTP requests, validate responses, chain API calls, generate test scripts (Python requests/pytest, curl, Postman collections). Use when: (1) testing API endpoints, (2) writing API test scripts, (3) validating response schema/status/data, (4) stress testing APIs, (5) generating Postman collections, (6) "接口测试", "API测试", "发请求", "压测接口", "生成Postman集合". NOT for: UI/browser testing (use web-tester), database testing, or test case design without execution (use test-case-gen).
openclaw skills install @zhanghengyi1986-afk/qa-api-testerTest, validate, and automate API interfaces.
✅ USE this skill when:
❌ DON'T use this skill when:
test-case-gen# GET
curl -s -w "\n%{http_code} %{time_total}s" \
-H "Authorization: Bearer $TOKEN" \
"https://api.example.com/users/1" | jq .
# POST with JSON body
curl -s -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"name":"test","email":"test@example.com"}' \
"https://api.example.com/users" | jq .
# PUT / PATCH / DELETE similar pattern
For each API response, verify:
When user asks for automated API tests, generate this structure:
"""API Test Suite - {module_name}
Generated by 虫探 🔍
"""
import pytest
import requests
BASE_URL = "https://api.example.com"
TOKEN = "" # Set via env or fixture
@pytest.fixture
def auth_headers():
return {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}
class TestUserAPI:
"""User module API tests"""
def test_get_user_success(self, auth_headers):
"""TC001: Get user by valid ID"""
resp = requests.get(f"{BASE_URL}/users/1", headers=auth_headers)
assert resp.status_code == 200
data = resp.json()
assert "id" in data
assert "name" in data
assert data["id"] == 1
def test_get_user_not_found(self, auth_headers):
"""TC002: Get user by non-existent ID"""
resp = requests.get(f"{BASE_URL}/users/99999", headers=auth_headers)
assert resp.status_code == 404
def test_create_user_success(self, auth_headers):
"""TC003: Create user with valid data"""
payload = {"name": "Test User", "email": "test@example.com"}
resp = requests.post(f"{BASE_URL}/users", json=payload, headers=auth_headers)
assert resp.status_code == 201
data = resp.json()
assert data["name"] == payload["name"]
def test_create_user_missing_field(self, auth_headers):
"""TC004: Create user missing required field"""
payload = {"name": "Test User"} # missing email
resp = requests.post(f"{BASE_URL}/users", json=payload, headers=auth_headers)
assert resp.status_code in (400, 422)
Save to workspace and run:
# Save script
# Run tests
cd ~/.openclaw/workspace && python3 -m pytest test_api.py -v --tb=short
For complex flows (login → create → verify → delete):
class TestUserWorkflow:
"""End-to-end user CRUD workflow"""
def test_full_crud_flow(self):
# Step 1: Login
resp = requests.post(f"{BASE_URL}/auth/login",
json={"username": "admin", "password": "pass"})
assert resp.status_code == 200
token = resp.json()["token"]
headers = {"Authorization": f"Bearer {token}"}
# Step 2: Create
user = requests.post(f"{BASE_URL}/users",
json={"name": "E2E Test", "email": "e2e@test.com"},
headers=headers)
assert user.status_code == 201
user_id = user.json()["id"]
# Step 3: Read & Verify
get_resp = requests.get(f"{BASE_URL}/users/{user_id}", headers=headers)
assert get_resp.status_code == 200
assert get_resp.json()["name"] == "E2E Test"
# Step 4: Update
update = requests.put(f"{BASE_URL}/users/{user_id}",
json={"name": "Updated"}, headers=headers)
assert update.status_code == 200
# Step 5: Delete
delete = requests.delete(f"{BASE_URL}/users/{user_id}", headers=headers)
assert delete.status_code in (200, 204)
# Step 6: Verify deleted
verify = requests.get(f"{BASE_URL}/users/{user_id}", headers=headers)
assert verify.status_code == 404
Generate Postman v2.1 collection JSON:
{
"info": {
"name": "API Test Collection",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"variable": [
{"key": "base_url", "value": "https://api.example.com"},
{"key": "token", "value": ""}
],
"item": [
{
"name": "Auth",
"item": [
{
"name": "Login",
"request": {
"method": "POST",
"url": "{{base_url}}/auth/login",
"header": [{"key": "Content-Type", "value": "application/json"}],
"body": {"mode": "raw", "raw": "{\"username\":\"admin\",\"password\":\"pass\"}"}
}
}
]
}
]
}
Always consider these for any API:
| Category | Test Points |
|---|---|
| Auth | No token, expired token, invalid token, wrong role |
| Input | Empty body, missing fields, wrong types, overflow values |
| Boundary | Max length strings, 0/negative numbers, future/past dates |
| Security | SQL injection, XSS in input, path traversal, IDOR |
| Concurrency | Duplicate requests, race conditions |
| Pagination | page=0, page=-1, huge page_size, beyond last page |
| Idempotency | Repeat same PUT/DELETE, check consistency |
When validating API response structure:
import jsonschema
user_schema = {
"type": "object",
"required": ["id", "name", "email"],
"properties": {
"id": {"type": "integer", "minimum": 1},
"name": {"type": "string", "minLength": 1},
"email": {"type": "string", "format": "email"},
"created_at": {"type": "string", "format": "date-time"}
},
"additionalProperties": False
}
def test_user_response_schema(auth_headers):
resp = requests.get(f"{BASE_URL}/users/1", headers=auth_headers)
jsonschema.validate(resp.json(), user_schema)
# Simple latency test (10 requests)
for i in $(seq 1 10); do
curl -s -o /dev/null -w "%{http_code} %{time_total}s\n" \
-H "Authorization: Bearer $TOKEN" \
"https://api.example.com/users"
done
# Concurrent requests (requires GNU parallel or xargs)
seq 1 50 | xargs -P 10 -I {} curl -s -o /dev/null -w "{}: %{http_code} %{time_total}s\n" \
"https://api.example.com/health"
管理多环境配置,避免硬编码:
import os
ENV_CONFIG = {
"dev": {"base_url": "https://dev-api.example.com", "token_env": "DEV_TOKEN"},
"staging": {"base_url": "https://staging-api.example.com", "token_env": "STG_TOKEN"},
"prod": {"base_url": "https://api.example.com", "token_env": "PROD_TOKEN"},
}
@pytest.fixture
def env():
name = os.getenv("TEST_ENV", "dev")
cfg = ENV_CONFIG[name]
cfg["token"] = os.getenv(cfg["token_env"], "")
return cfg
Run with: TEST_ENV=staging python3 -m pytest test_api.py -v