areas/software/qa/skills/api-testing/SKILL.md
Write API integration tests and consumer-driven contract tests (Pact) for service boundaries.
npx skillsauth add sawrus/agent-guides api-testingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Expertise: API integration tests (supertest/httpx), contract testing (Pact), auth flows, error path coverage.
import pytest
import pytest_asyncio
from httpx import AsyncClient
# conftest.py — reusable fixtures
@pytest_asyncio.fixture
async def client(app, db_session) -> AsyncClient:
async with AsyncClient(app=app, base_url="http://test") as c:
yield c
@pytest_asyncio.fixture
async def auth_client(client, create_user) -> AsyncClient:
user = await create_user(role="viewer")
response = await client.post("/auth/token",
data={"username": user.email, "password": "test_password"})
token = response.json()["access_token"]
client.headers["Authorization"] = f"Bearer {token}"
return client
# Test — covers happy path + all error cases
class TestCreateOrder:
async def test_creates_order_for_authenticated_user(self, auth_client, product_factory):
product = await product_factory(price="29.99", stock=10)
response = await auth_client.post("/api/v1/orders", json={
"items": [{"product_id": product.id, "quantity": 2}]
})
assert response.status_code == 201
body = response.json()
assert body["status"] == "pending"
assert body["total_amount"] == "59.98"
assert body["id"].startswith("ord_")
async def test_returns_401_without_auth(self, client):
response = await client.post("/api/v1/orders", json={"items": []})
assert response.status_code == 401
async def test_returns_400_when_product_out_of_stock(self, auth_client, product_factory):
product = await product_factory(stock=0)
response = await auth_client.post("/api/v1/orders", json={
"items": [{"product_id": product.id, "quantity": 1}]
})
assert response.status_code == 400
assert response.json()["error"]["code"] == "PRODUCT_OUT_OF_STOCK"
async def test_returns_404_for_nonexistent_product(self, auth_client):
response = await auth_client.post("/api/v1/orders", json={
"items": [{"product_id": "prod_nonexistent", "quantity": 1}]
})
assert response.status_code == 404
Every endpoint must have tests for:
Use when: two services are developed by different teams and must agree on API shape.
# consumer side (frontend/service-b defines expectations)
from pact import Consumer, Provider
pact = Consumer("order-consumer").has_pact_with(Provider("order-service"))
def test_get_order_contract():
expected = {
"id": "ord_123",
"status": "pending",
"total_amount": Term(r"^\d+\.\d{2}$", "59.98"),
}
(pact
.given("order ord_123 exists")
.upon_receiving("a request to get order ord_123")
.with_request("GET", "/api/v1/orders/ord_123")
.will_respond_with(200, body=Like(expected)))
with pact:
response = order_api_client.get_order("ord_123")
assert response["status"] == "pending"
# Pact saves contract to pacts/ directory
# Provider side verifies against saved pact file
# Run in CI after consumer tests generate pact files
async def test_list_orders_pagination(auth_client, order_factory):
# Create 25 orders for cursor-based pagination test
orders = [await order_factory(user_id=current_user.id) for _ in range(25)]
# First page
response = await auth_client.get("/api/v1/orders?limit=10")
assert response.status_code == 200
body = response.json()
assert len(body["items"]) == 10
assert body["next_cursor"] is not None
# Second page using cursor
response2 = await auth_client.get(f"/api/v1/orders?limit=10&cursor={body['next_cursor']}")
assert len(response2.json()["items"]) == 10
# Items should not overlap with first page
first_ids = {o["id"] for o in body["items"]}
second_ids = {o["id"] for o in response2.json()["items"]}
assert first_ids.isdisjoint(second_ids)
# Last page — fewer items, no next cursor
response3 = await auth_client.get(f"/api/v1/orders?limit=10&cursor={response2.json()['next_cursor']}")
assert len(response3.json()["items"]) == 5
assert response3.json()["next_cursor"] is None
testing
QA Expert for writing E2E tests, test scenarios, test plans, and ensuring test coverage quality.
development
Expert UI/UX design intelligence for creating distinctive, high-craft, and mobile-first interfaces. Focuses on premium aesthetics, touch-first ergonomics, and Flutter performance.
development
Code Review Expert for static analysis, security auditing, architecture review, and ensuring code quality standards.
development
Babysit a GitHub pull request after creation by continuously polling review comments, CI checks/workflow runs, and mergeability state until the PR is merged/closed or user help is required. Diagnose failures, retry likely flaky failures up to 3 times, auto-fix/push branch-related issues when appropriate, and keep watching open PRs so fresh review feedback is surfaced promptly. Use when the user asks Codex to monitor a PR, watch CI, handle review comments, or keep an eye on failures and feedback on an open PR.