
Api Testing Patterns
- 554 installs
- 433 repo stars
- Updated August 4, 2026
- proffesor-for-testing/agentic-qe
api-testing-patterns is a Claude Code skill that designs contract, integration, and negative-path API tests for REST or GraphQL services for developers who must validate auth, pagination, idempotency, and error responses
About
api-testing-patterns is a Testing & QA skill from proffesor-for-testing/agentic-qe that guides developers through designing API test suites for REST and GraphQL backends. The skill covers contract tests for schema stability, integration flows across services, and negative-path cases including auth failures, pagination edge cases, idempotency retries, and structured error assertions. Developers reach for api-testing-patterns when an API surface grows beyond happy-path smoke tests and needs systematic coverage before CI or production release. Outputs are concrete test scenarios and assertion patterns aligned with release gates rather than exploratory manual calls.
- REST and GraphQL contract checks
- Auth, rate-limit, and error-path coverage
- Idempotency and pagination scenarios
- CI-friendly assertion patterns
- Mocking and test-data setup guidance
Api Testing Patterns by the numbers
- 554 all-time installs (skills.sh)
- +8 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #606 of 2,153 Testing & QA skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/proffesor-for-testing/agentic-qe --skill api-testing-patternsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 554 |
|---|---|
| repo stars | ★ 433 |
| Last updated | August 4, 2026 |
| Repository | proffesor-for-testing/agentic-qe ↗ |
How do you test REST and GraphQL APIs before release?
Design contract, integration, and negative-path API tests for REST or GraphQL services before release, including auth, pagination, idempotency, and error-shape assertions.
Who is it for?
Backend developers shipping REST or GraphQL services who need structured contract and negative-path coverage before release.
Skip if: Frontend-only teams or projects needing browser E2E UI tests instead of HTTP API contract and integration suites.
When should I use this skill?
User asks for API contract tests, GraphQL integration tests, auth or pagination test cases, idempotency checks, or error response assertions.
What you get
API contract test cases, integration scenarios, negative-path suites, and error-shape assertion patterns
- API test scenarios
- Contract test cases
- Negative-path assertion patterns
Files
API Testing Patterns
<default_to_action> When testing APIs or designing API test strategy: 1. IDENTIFY testing level: contract, integration, or component 2. TEST the contract, not implementation (consumer perspective) 3. VALIDATE auth, input, errors, idempotency, concurrency 4. AUTOMATE in CI/CD with schema validation 5. MONITOR production APIs for contract drift
Quick Pattern Selection:
- Microservices → Consumer-driven contracts (Pact)
- REST APIs → CRUD + pagination + filtering tests
- GraphQL → Query validation + complexity limits
- External deps → Mock with component testing
- Performance → Load test critical endpoints
Critical Success Factors:
- APIs are contracts - test from consumer perspective
- Always test error scenarios, not just happy paths
- Version your API tests to prevent breaking changes
</default_to_action>
Quick Reference Card
When to Use
- Testing REST or GraphQL APIs
- Validating microservice contracts
- Designing API test strategies
- Preventing breaking API changes
Testing Levels
| Level | Purpose | Dependencies | Speed |
|---|---|---|---|
| Contract | Provider-consumer agreement | None | Fast |
| Component | API in isolation | Mocked | Fast |
| Integration | Real dependencies | Database, services | Slower |
Critical Test Scenarios
| Scenario | Must Test | Example |
|---|---|---|
| Auth | 401/403 handling | Expired token, wrong user |
| Input | 400 validation | Missing fields, wrong types |
| Errors | 500 graceful handling | DB down, timeout |
| Idempotency | Duplicate prevention | Same idempotency key |
| Concurrency | Race conditions | Parallel checkout |
Tools
- Contract: Pact, Spring Cloud Contract
- REST: Supertest, REST-assured, Playwright
- Load: k6, Artillery, JMeter
Agent Coordination
qe-api-contract-validator: Validate contracts, detect breaking changesqe-test-generator: Generate tests from OpenAPI specqe-performance-tester: Load test endpointsqe-security-scanner: API security testing
---
Contract Testing
Pattern: Consumer-Driven Contracts
// Consumer defines expectations
const contract = {
request: { method: 'POST', path: '/orders', body: { productId: 'abc', quantity: 2 } },
response: { status: 201, body: { orderId: 'string', total: 'number' } }
};
// Provider must fulfill
test('order API meets contract', async () => {
const response = await api.post('/orders', { productId: 'abc', quantity: 2 });
expect(response.status).toBe(201);
expect(response.body).toMatchSchema({
orderId: expect.any(String),
total: expect.any(Number)
});
});When: Microservices, distributed systems, third-party integrations
---
Critical Test Patterns
Authentication & Authorization
describe('Auth', () => {
it('rejects without token', async () => {
expect((await api.get('/orders')).status).toBe(401);
});
it('rejects expired token', async () => {
const expired = generateExpiredToken();
expect((await api.get('/orders', { headers: { Authorization: `Bearer ${expired}` } })).status).toBe(401);
});
it('blocks cross-user access', async () => {
const userAToken = generateToken({ userId: 'A' });
expect((await api.get('/orders/user-B-order', { headers: { Authorization: `Bearer ${userAToken}` } })).status).toBe(403);
});
});Input Validation
describe('Validation', () => {
it('validates required fields', async () => {
const response = await api.post('/orders', { quantity: 2 }); // Missing productId
expect(response.status).toBe(400);
expect(response.body.errors).toContain('productId is required');
});
it('validates types', async () => {
expect((await api.post('/orders', { productId: 'abc', quantity: 'two' })).status).toBe(400);
});
it('validates ranges', async () => {
expect((await api.post('/orders', { productId: 'abc', quantity: -5 })).status).toBe(400);
});
});Idempotency
it('prevents duplicates with idempotency key', async () => {
const key = 'unique-123';
const data = { productId: 'abc', quantity: 2 };
const r1 = await api.post('/orders', data, { headers: { 'Idempotency-Key': key } });
const r2 = await api.post('/orders', data, { headers: { 'Idempotency-Key': key } });
expect(r1.body.orderId).toBe(r2.body.orderId); // Same order
});Concurrency
it('handles race condition on inventory', async () => {
const promises = Array(10).fill().map(() =>
api.post('/orders', { productId: 'abc', quantity: 1 })
);
const responses = await Promise.all(promises);
const successful = responses.filter(r => r.status === 201);
const inventory = await db.inventory.findById('abc');
expect(inventory.quantity).toBe(initialQuantity - successful.length);
});---
REST CRUD Pattern
describe('Product CRUD', () => {
let productId;
it('CREATE', async () => {
const r = await api.post('/products', { name: 'Widget', price: 10 });
expect(r.status).toBe(201);
productId = r.body.id;
});
it('READ', async () => {
const r = await api.get(`/products/${productId}`);
expect(r.body.name).toBe('Widget');
});
it('UPDATE', async () => {
const r = await api.put(`/products/${productId}`, { price: 12 });
expect(r.body.price).toBe(12);
});
it('DELETE', async () => {
expect((await api.delete(`/products/${productId}`)).status).toBe(204);
expect((await api.get(`/products/${productId}`)).status).toBe(404);
});
});---
Best Practices
✅ Do This
- Test from consumer perspective
- Use schema validation (not exact values)
- Test error scenarios extensively
- Version API tests
- Automate in CI/CD
❌ Avoid This
- Testing implementation, not contract
- Ignoring HTTP semantics (status codes)
- No negative testing
- Asserting on field order or extra fields
- Slow tests (mock external services)
---
Agent-Assisted API Testing
// Validate contracts
await Task("Contract Validation", {
spec: 'openapi.yaml',
endpoint: '/orders',
checkBreakingChanges: true
}, "qe-api-contract-validator");
// Generate tests from spec
await Task("Generate API Tests", {
spec: 'openapi.yaml',
coverage: 'comprehensive',
include: ['happy-paths', 'input-validation', 'auth-scenarios', 'error-handling']
}, "qe-test-generator");
// Load test
await Task("API Load Test", {
endpoint: '/orders',
rps: 1000,
duration: '5min'
}, "qe-performance-tester");
// Security scan
await Task("API Security Scan", {
spec: 'openapi.yaml',
checks: ['sql-injection', 'xss', 'broken-auth', 'rate-limiting']
}, "qe-security-scanner");---
Agent Coordination Hints
Memory Namespace
aqe/api-testing/
├── contracts/* - API contract definitions
├── generated-tests/* - Generated test suites
├── validation/* - Contract validation results
└── performance/* - Load test resultsFleet Coordination
const apiFleet = await FleetManager.coordinate({
strategy: 'contract-testing',
agents: ['qe-api-contract-validator', 'qe-test-generator', 'qe-test-executor'],
topology: 'mesh'
});
await apiFleet.execute({
services: [
{ name: 'orders-api', consumers: ['checkout-ui', 'admin-api'] },
{ name: 'payment-api', consumers: ['orders-api'] }
]
});---
Related Skills
- agentic-quality-engineering - API testing with agents
- tdd-london-chicago - London school for API testing
- performance-testing - API load testing
- security-testing - API security validation
- contract-testing - Consumer-driven contracts deep dive
---
Remember
API testing = verifying contracts and behavior, not implementation. Focus on what matters to consumers: correct responses, proper error handling, acceptable performance.
With Agents: Agents automate contract validation, generate comprehensive test suites from specs, and monitor production APIs for drift. Use agents to maintain API quality at scale.
Gotchas
- Agent generates tests against documented API, not actual API — always validate against running service first
- Auth tokens expire between test runs — use fixtures with long-lived tokens or refresh before each suite
- Rate limiting in CI causes intermittent failures — add retry with exponential backoff for 429 responses
- GraphQL introspection may be disabled in production — test against staging schema, not production endpoint
- Idempotency tests need unique request IDs per run — hardcoded IDs cause false passes on retry
{
"$schema": "./config-schema.json",
"_description": "API Testing configuration. Auto-created on first run. Edit to customize.",
"api_type": null,
"auth_type": null,
"base_url": null,
"options": {
"validateSchemaOnEveryRequest": true,
"retryOn429": true,
"retryDelay": 1000,
"timeout": 30000
},
"_setupPrompt": "If api_type is null, ask: 'What type of API are you testing? (rest/graphql/grpc)'. If auth_type is null, ask: 'What authentication does the API use? (bearer/oauth2/api-key/basic/none)'. If base_url is null, ask: 'What is the base URL for the API under test?'"
}
# =============================================================================
# AQE API Testing Patterns Skill Evaluation Test Suite v1.0.0
# Per ADR-056 - Trust Tier 3 Validation
# =============================================================================
#
# This evaluation suite validates the api-testing-patterns skill behavior:
# - REST API testing patterns
# - GraphQL API testing patterns
# - Contract testing (Pact)
# - Authentication/Authorization testing
# - Error handling and validation
# - Pagination and filtering
# - Integration with QE agents
#
# Schema: .claude/skills/.validation/schemas/skill-eval.schema.json
# Runner: scripts/run-skill-eval.ts
#
# =============================================================================
skill: api-testing-patterns
version: 1.0.0
description: >
Comprehensive evaluation suite for the api-testing-patterns skill.
Tests core API testing patterns across REST, GraphQL, contract testing,
and various critical scenarios to ensure consistent, high-quality output
across multiple models.
# =============================================================================
# Multi-Model Configuration
# =============================================================================
models_to_test:
- claude-sonnet-4-6 # Primary (high accuracy expected)
- claude-haiku-4-5 # Fast model (minimum quality floor)
# =============================================================================
# MCP Integration Configuration
# =============================================================================
mcp_integration:
enabled: true
namespace: skill-validation
query_patterns: true
track_outcomes: true
store_patterns: true
share_learning: true
update_quality_gate: true
target_agents:
- qe-learning-coordinator
- qe-queen-coordinator
- qe-api-contract-validator
# =============================================================================
# ReasoningBank Learning Configuration
# =============================================================================
learning:
store_success_patterns: true
store_failure_patterns: true
pattern_ttl_days: 90
min_confidence_to_store: 0.7
cross_model_comparison: true
# =============================================================================
# Result Format Configuration
# =============================================================================
result_format:
json_output: true
markdown_report: true
include_raw_output: false
include_timing: true
include_token_usage: true
# =============================================================================
# Environment Setup
# =============================================================================
setup:
required_tools:
- jq
environment_variables:
AQE_VALIDATION_MODE: "eval"
fixtures:
- name: sample_openapi_spec
path: fixtures/openapi-sample.yaml
content: |
openapi: "3.0.3"
info:
title: Sample API
version: "1.0.0"
paths:
/users:
get:
operationId: getUsers
responses:
"200":
description: List of users
/users/{id}:
get:
operationId: getUserById
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
"200":
description: User found
"404":
description: User not found
- name: sample_express_api
path: fixtures/express-api.js
content: |
const express = require('express');
const app = express();
app.get('/api/users', (req, res) => {
const users = db.query('SELECT * FROM users');
res.json(users);
});
app.post('/api/orders', (req, res) => {
const { productId, quantity } = req.body;
if (!productId) return res.status(400).json({ error: 'productId required' });
const order = orderService.create({ productId, quantity });
res.status(201).json(order);
});
app.get('/api/orders/:id', auth.required, (req, res) => {
const order = orderService.findById(req.params.id);
if (!order) return res.status(404).json({ error: 'Not found' });
if (order.userId !== req.user.id) return res.status(403).json({ error: 'Forbidden' });
res.json(order);
});
# =============================================================================
# Test Cases
# =============================================================================
test_cases:
# -------------------------------------------------------------------------
# Basic Functionality Tests
# -------------------------------------------------------------------------
- id: tc001_basic_rest_analysis
description: "Skill analyzes basic REST API and identifies test patterns"
category: basic
priority: critical
input:
prompt: |
Analyze this REST API endpoint and recommend API testing patterns:
```javascript
app.get('/api/users', (req, res) => {
const users = db.query('SELECT * FROM users');
res.json(users);
});
```
context:
language: javascript
framework: express
apiType: rest
expected_output:
must_contain:
- "GET"
- "test"
- "users"
- "response"
must_not_contain:
- "unable to analyze"
- "error"
- "TODO"
validation:
schema_check: true
keyword_match_threshold: 0.8
reasoning_quality_min: 0.7
- id: tc002_contract_testing_recommendation
description: "Skill recommends consumer-driven contracts for microservices"
category: contract
priority: critical
input:
prompt: |
I have a checkout-service that calls orders-api and payments-api.
What API testing patterns should I use to ensure these services
work correctly together?
context:
architecture: microservices
services:
- checkout-service
- orders-api
- payments-api
expected_output:
must_contain:
- "contract"
- "consumer"
- "provider"
must_match_regex:
- "(?i)(pact|consumer-driven|contract test)"
validation:
schema_check: true
keyword_match_threshold: 0.8
reasoning_quality_min: 0.8
- id: tc003_graphql_testing_patterns
description: "Skill provides GraphQL-specific testing patterns"
category: graphql
priority: high
input:
prompt: |
Analyze this GraphQL API for testing patterns:
```graphql
type Query {
user(id: ID!): User
users(limit: Int, offset: Int): [User!]!
}
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User
}
```
context:
apiType: graphql
expected_output:
must_contain:
- "query"
- "mutation"
- "GraphQL"
must_not_contain:
- "REST"
- "HTTP method"
validation:
schema_check: true
keyword_match_threshold: 0.7
# -------------------------------------------------------------------------
# Authentication Testing
# -------------------------------------------------------------------------
- id: tc004_auth_testing_patterns
description: "Skill identifies authentication testing scenarios"
category: auth
priority: critical
input:
code: |
app.get('/api/orders/:id', auth.required, (req, res) => {
const order = orderService.findById(req.params.id);
if (!order) return res.status(404).json({ error: 'Not found' });
if (order.userId !== req.user.id) return res.status(403).json({ error: 'Forbidden' });
res.json(order);
});
context:
language: javascript
framework: express
expected_output:
must_contain:
- "401"
- "403"
- "auth"
- "token"
must_match_regex:
- "(?i)(unauthorized|forbidden|access)"
validation:
schema_check: true
keyword_match_threshold: 0.8
grading_rubric:
completeness: 0.4
accuracy: 0.4
actionability: 0.2
- id: tc005_expired_token_scenario
description: "Skill identifies expired token testing scenario"
category: auth
priority: high
input:
prompt: |
For a JWT-authenticated API, what test scenarios should I cover
for authentication failures?
context:
authType: jwt
expected_output:
must_contain:
- "expired"
- "invalid"
- "token"
must_match_regex:
- "(?i)(expire|timeout|invalid.*token)"
validation:
schema_check: true
keyword_match_threshold: 0.7
# -------------------------------------------------------------------------
# Error Handling Tests
# -------------------------------------------------------------------------
- id: tc006_error_handling_patterns
description: "Skill recommends error handling test scenarios"
category: error_handling
priority: high
input:
prompt: |
What error handling scenarios should I test for this API endpoint?
```javascript
app.post('/api/orders', async (req, res) => {
try {
const order = await orderService.create(req.body);
res.status(201).json(order);
} catch (error) {
if (error.code === 'VALIDATION_ERROR') {
return res.status(400).json({ error: error.message });
}
res.status(500).json({ error: 'Internal server error' });
}
});
```
context:
language: javascript
expected_output:
must_contain:
- "400"
- "500"
- "error"
- "validation"
must_not_contain:
- "no errors"
- "perfect"
validation:
schema_check: true
keyword_match_threshold: 0.8
- id: tc007_input_validation_testing
description: "Skill identifies input validation test cases"
category: validation
priority: high
input:
code: |
app.post('/api/users', (req, res) => {
const { email, password, age } = req.body;
if (!email) return res.status(400).json({ error: 'Email required' });
if (!password || password.length < 8) return res.status(400).json({ error: 'Password must be 8+ chars' });
if (age && (age < 0 || age > 150)) return res.status(400).json({ error: 'Invalid age' });
// Create user...
});
context:
language: javascript
expected_output:
must_contain:
- "required"
- "validation"
- "email"
- "password"
must_match_regex:
- "(?i)(boundary|range|length)"
finding_count:
min: 1
validation:
schema_check: true
keyword_match_threshold: 0.7
# -------------------------------------------------------------------------
# Pagination and Filtering Tests
# -------------------------------------------------------------------------
- id: tc008_pagination_testing
description: "Skill recommends pagination testing patterns"
category: pagination
priority: medium
input:
prompt: |
This API supports pagination. What test scenarios should I cover?
```javascript
app.get('/api/products', (req, res) => {
const { page = 1, limit = 20, sort = 'name' } = req.query;
const products = productService.find({ page, limit, sort });
res.json({
data: products,
pagination: { page, limit, total: products.total }
});
});
```
context:
language: javascript
expected_output:
must_contain:
- "page"
- "limit"
- "boundary"
must_match_regex:
- "(?i)(first.*page|last.*page|empty|zero)"
validation:
schema_check: true
keyword_match_threshold: 0.7
- id: tc009_filtering_testing
description: "Skill recommends filter/search testing patterns"
category: filtering
priority: medium
input:
prompt: |
How should I test filtering and search functionality?
```javascript
app.get('/api/products', (req, res) => {
const { category, minPrice, maxPrice, search } = req.query;
const filters = { category, minPrice, maxPrice, search };
const products = productService.search(filters);
res.json(products);
});
```
expected_output:
must_contain:
- "filter"
- "search"
must_match_regex:
- "(?i)(empty.*result|no.*match|invalid.*filter)"
validation:
schema_check: true
# -------------------------------------------------------------------------
# Idempotency and Concurrency Tests
# -------------------------------------------------------------------------
- id: tc010_idempotency_testing
description: "Skill identifies idempotency testing patterns"
category: idempotency
priority: high
input:
prompt: |
My payment API uses idempotency keys. What test scenarios should I cover?
```javascript
app.post('/api/payments', async (req, res) => {
const idempotencyKey = req.headers['idempotency-key'];
if (idempotencyKey) {
const existing = await cache.get(idempotencyKey);
if (existing) return res.json(existing);
}
const payment = await paymentService.process(req.body);
if (idempotencyKey) await cache.set(idempotencyKey, payment);
res.status(201).json(payment);
});
```
expected_output:
must_contain:
- "idempotency"
- "duplicate"
- "key"
must_match_regex:
- "(?i)(same.*result|repeat|retry)"
validation:
schema_check: true
keyword_match_threshold: 0.8
- id: tc011_concurrency_testing
description: "Skill identifies race condition testing scenarios"
category: concurrency
priority: high
input:
prompt: |
How do I test for race conditions in this inventory API?
```javascript
app.post('/api/orders', async (req, res) => {
const product = await productService.findById(req.body.productId);
if (product.stock < req.body.quantity) {
return res.status(400).json({ error: 'Insufficient stock' });
}
await productService.decrementStock(req.body.productId, req.body.quantity);
const order = await orderService.create(req.body);
res.status(201).json(order);
});
```
expected_output:
must_contain:
- "race"
- "concurrent"
- "parallel"
must_match_regex:
- "(?i)(lock|atomic|transaction)"
validation:
schema_check: true
keyword_match_threshold: 0.7
# -------------------------------------------------------------------------
# Integration Testing Patterns
# -------------------------------------------------------------------------
- id: tc012_integration_test_structure
description: "Skill recommends proper integration test structure"
category: integration
priority: high
input:
prompt: |
I need to write integration tests for my Express API that connects
to PostgreSQL and Redis. What patterns should I follow?
context:
framework: express
database: postgresql
cache: redis
expected_output:
must_contain:
- "database"
- "setup"
- "teardown"
must_match_regex:
- "(?i)(before|after|cleanup|seed)"
recommendation_count:
min: 1
validation:
schema_check: true
grading_rubric:
completeness: 0.3
accuracy: 0.4
actionability: 0.3
- id: tc013_supertest_pattern
description: "Skill demonstrates supertest usage for Node.js APIs"
category: integration
priority: medium
input:
prompt: |
Show me how to use supertest for testing this Express API endpoint:
```javascript
app.post('/api/users', (req, res) => {
const user = userService.create(req.body);
res.status(201).json(user);
});
```
context:
framework: express
testFramework: jest
expected_output:
must_contain:
- "supertest"
- "expect"
- "201"
must_match_regex:
- "(?i)(request|post|send)"
validation:
schema_check: true
# -------------------------------------------------------------------------
# Negative Tests (Should NOT find issues)
# -------------------------------------------------------------------------
- id: tc014_well_tested_api
description: "Skill acknowledges well-tested API without false positives"
category: negative
priority: high
input:
prompt: |
This API already has comprehensive tests. What additional tests might be needed?
- Unit tests for all service methods
- Integration tests for all endpoints
- Contract tests with all consumers
- Load tests for high-traffic endpoints
- Security tests for auth flows
context:
testCoverage: "comprehensive"
expected_output:
must_contain:
- "comprehensive"
must_not_contain:
- "critical gap"
- "missing"
- "no tests"
validation:
schema_check: true
finding_count:
max: 3 # Allow minor suggestions only
# -------------------------------------------------------------------------
# Edge Cases
# -------------------------------------------------------------------------
- id: tc015_empty_api_spec
description: "Skill handles empty or minimal API gracefully"
category: edge_cases
priority: medium
input:
prompt: "Analyze this API for testing patterns:"
context:
apiSpec: null
expected_output:
must_contain:
- "provide"
- "API"
must_not_contain:
- "exception"
- "crash"
validation:
schema_check: true
allow_partial: true
- id: tc016_large_api_spec
description: "Skill handles large API specifications"
category: edge_cases
priority: medium
skip: false
input:
file_path: fixtures/openapi-sample.yaml
context:
apiType: rest
expected_output:
must_contain:
- "endpoint"
- "test"
validation:
schema_check: true
timeout_ms: 60000
# =============================================================================
# Success Criteria
# =============================================================================
success_criteria:
# Minimum percentage of tests that must pass
pass_rate: 0.90
# Critical tests must have 100% pass rate
critical_pass_rate: 1.0
# Average reasoning quality across all tests
avg_reasoning_quality: 0.7
# Maximum time for entire suite (5 minutes)
max_execution_time_ms: 300000
# Maximum variance between different models (15%)
cross_model_variance: 0.15
# =============================================================================
# Metadata
# =============================================================================
metadata:
author: "@agentic-qe"
created: "2026-02-02"
last_updated: "2026-02-02"
coverage_target: >
Core API testing patterns including REST, GraphQL, contract testing,
authentication, error handling, pagination, idempotency, and concurrency.
Tests 16 scenarios across 8 categories.
adr_reference: "ADR-056"
trust_tier: 3
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://agentic-qe.dev/schemas/skills/api-testing-patterns/output.json",
"title": "API Testing Patterns Skill Output Schema",
"description": "Schema for API testing patterns skill output. Extends the base skill-output template with API-specific structures for contract, integration, component, and load testing.",
"allOf": [
{
"$comment": "Base schema structure from skill-output.template.json",
"type": "object",
"required": ["skillName", "version", "timestamp", "status", "trustTier", "output"],
"properties": {
"skillName": {
"type": "string",
"const": "api-testing-patterns"
},
"version": {
"type": "string",
"pattern": "^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9]+)?$"
},
"timestamp": {
"type": "string",
"format": "date-time"
},
"status": {
"type": "string",
"enum": ["success", "partial", "failed", "skipped"]
},
"trustTier": {
"type": "integer",
"minimum": 0,
"maximum": 3
}
}
}
],
"properties": {
"output": {
"type": "object",
"required": ["summary", "testingLevel", "endpointCoverage"],
"properties": {
"summary": {
"type": "string",
"minLength": 10,
"maxLength": 2000,
"description": "Human-readable summary of API testing analysis"
},
"testingLevel": {
"type": "string",
"enum": ["contract", "component", "integration", "e2e", "load", "mixed"],
"description": "Primary API testing level recommended"
},
"apiType": {
"type": "string",
"enum": ["rest", "graphql", "grpc", "websocket", "mixed"],
"description": "Type of API being tested"
},
"endpointCoverage": {
"$ref": "#/$defs/endpointCoverage",
"description": "Coverage analysis for API endpoints"
},
"testRecommendations": {
"type": "array",
"items": {
"$ref": "#/$defs/testRecommendation"
},
"maxItems": 50,
"description": "Recommended API tests to implement"
},
"contractTests": {
"$ref": "#/$defs/contractTestSuite",
"description": "Contract testing specifications"
},
"integrationTests": {
"$ref": "#/$defs/integrationTestSuite",
"description": "Integration testing specifications"
},
"componentTests": {
"$ref": "#/$defs/componentTestSuite",
"description": "Component testing specifications"
},
"loadTests": {
"$ref": "#/$defs/loadTestSuite",
"description": "Load/performance testing specifications"
},
"securityTests": {
"$ref": "#/$defs/securityTestSuite",
"description": "API security testing specifications"
},
"mockConfigurations": {
"type": "array",
"items": {
"$ref": "#/$defs/mockConfiguration"
},
"maxItems": 100,
"description": "Mock/stub configurations for testing"
},
"findings": {
"type": "array",
"items": {
"$ref": "#/$defs/apiFinding"
},
"maxItems": 200,
"description": "API testing gaps and issues found"
},
"recommendations": {
"type": "array",
"items": {
"$ref": "#/$defs/recommendation"
},
"maxItems": 50,
"description": "Actionable recommendations for API testing improvement"
},
"metrics": {
"$ref": "#/$defs/apiMetrics",
"description": "Quantitative API testing metrics"
},
"categories": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/categoryScore"
},
"description": "Scores by API testing category"
}
}
},
"metadata": {
"type": "object",
"properties": {
"executionTimeMs": {
"type": "integer",
"minimum": 0
},
"toolsUsed": {
"type": "array",
"items": { "type": "string" }
},
"agentId": {
"type": "string",
"pattern": "^qe-[a-z][a-z0-9-]*$"
},
"apiSpecPath": {
"type": "string",
"description": "Path to OpenAPI/GraphQL spec analyzed"
},
"consumers": {
"type": "array",
"items": { "type": "string" },
"description": "Consumer services for contract testing"
},
"providers": {
"type": "array",
"items": { "type": "string" },
"description": "Provider services for contract testing"
}
}
},
"validation": {
"type": "object",
"properties": {
"schemaValid": { "type": "boolean" },
"contentValid": { "type": "boolean" },
"confidence": {
"type": "number",
"minimum": 0,
"maximum": 1
}
}
},
"learning": {
"type": "object",
"properties": {
"patternsDetected": {
"type": "array",
"items": { "type": "string" }
},
"reward": {
"type": "number",
"minimum": 0,
"maximum": 1
}
}
}
},
"$defs": {
"endpointCoverage": {
"type": "object",
"description": "API endpoint coverage analysis",
"required": ["totalEndpoints", "coveredEndpoints"],
"properties": {
"totalEndpoints": {
"type": "integer",
"minimum": 0,
"description": "Total number of API endpoints"
},
"coveredEndpoints": {
"type": "integer",
"minimum": 0,
"description": "Number of endpoints with test coverage"
},
"coveragePercentage": {
"type": "number",
"minimum": 0,
"maximum": 100,
"description": "Percentage of endpoints with coverage"
},
"byHttpMethod": {
"$ref": "#/$defs/httpMethodCoverage"
},
"byStatusCode": {
"$ref": "#/$defs/statusCodeCoverage"
},
"uncoveredEndpoints": {
"type": "array",
"items": {
"$ref": "#/$defs/endpoint"
},
"description": "List of endpoints without coverage"
},
"partiallyCoveredEndpoints": {
"type": "array",
"items": {
"$ref": "#/$defs/endpoint"
},
"description": "Endpoints with incomplete coverage"
}
}
},
"httpMethodCoverage": {
"type": "object",
"description": "Coverage breakdown by HTTP method",
"properties": {
"GET": { "$ref": "#/$defs/methodCoverageDetail" },
"POST": { "$ref": "#/$defs/methodCoverageDetail" },
"PUT": { "$ref": "#/$defs/methodCoverageDetail" },
"PATCH": { "$ref": "#/$defs/methodCoverageDetail" },
"DELETE": { "$ref": "#/$defs/methodCoverageDetail" },
"OPTIONS": { "$ref": "#/$defs/methodCoverageDetail" },
"HEAD": { "$ref": "#/$defs/methodCoverageDetail" }
}
},
"methodCoverageDetail": {
"type": "object",
"properties": {
"total": { "type": "integer", "minimum": 0 },
"covered": { "type": "integer", "minimum": 0 },
"percentage": { "type": "number", "minimum": 0, "maximum": 100 }
}
},
"statusCodeCoverage": {
"type": "object",
"description": "Coverage breakdown by HTTP status code category",
"properties": {
"2xx": { "$ref": "#/$defs/methodCoverageDetail" },
"3xx": { "$ref": "#/$defs/methodCoverageDetail" },
"4xx": { "$ref": "#/$defs/methodCoverageDetail" },
"5xx": { "$ref": "#/$defs/methodCoverageDetail" }
}
},
"endpoint": {
"type": "object",
"description": "API endpoint definition",
"required": ["method", "path"],
"properties": {
"method": {
"type": "string",
"enum": ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"]
},
"path": {
"type": "string",
"pattern": "^/.*$",
"description": "API endpoint path"
},
"operationId": {
"type": "string",
"description": "OpenAPI operationId"
},
"summary": {
"type": "string",
"description": "Endpoint description"
},
"tags": {
"type": "array",
"items": { "type": "string" }
},
"parameters": {
"type": "array",
"items": {
"$ref": "#/$defs/parameter"
}
},
"requestBody": {
"$ref": "#/$defs/requestBody"
},
"responses": {
"type": "object",
"additionalProperties": {
"$ref": "#/$defs/response"
}
},
"security": {
"type": "array",
"items": { "type": "object" }
}
}
},
"parameter": {
"type": "object",
"required": ["name", "in"],
"properties": {
"name": { "type": "string" },
"in": {
"type": "string",
"enum": ["query", "path", "header", "cookie"]
},
"required": { "type": "boolean" },
"schema": { "type": "object" }
}
},
"requestBody": {
"type": "object",
"properties": {
"required": { "type": "boolean" },
"contentType": { "type": "string" },
"schema": { "type": "object" }
}
},
"response": {
"type": "object",
"properties": {
"statusCode": { "type": "integer" },
"description": { "type": "string" },
"contentType": { "type": "string" },
"schema": { "type": "object" }
}
},
"testRecommendation": {
"type": "object",
"description": "Recommended API test to implement",
"required": ["id", "testType", "endpoint", "description"],
"properties": {
"id": {
"type": "string",
"pattern": "^API-\\d{3,6}$",
"description": "Recommendation ID"
},
"testType": {
"type": "string",
"enum": ["contract", "integration", "component", "load", "security", "e2e"],
"description": "Type of test recommended"
},
"endpoint": {
"$ref": "#/$defs/endpoint"
},
"description": {
"type": "string",
"maxLength": 500
},
"priority": {
"type": "string",
"enum": ["critical", "high", "medium", "low"]
},
"scenarios": {
"type": "array",
"items": {
"$ref": "#/$defs/testScenario"
},
"description": "Specific test scenarios to implement"
},
"codeExample": {
"type": "string",
"maxLength": 5000,
"description": "Example test code"
},
"framework": {
"type": "string",
"enum": ["supertest", "pact", "rest-assured", "playwright", "k6", "artillery", "jest", "mocha", "vitest"],
"description": "Recommended testing framework"
}
}
},
"testScenario": {
"type": "object",
"description": "Specific test scenario",
"required": ["name", "category"],
"properties": {
"name": {
"type": "string"
},
"category": {
"type": "string",
"enum": ["happy-path", "error-handling", "auth", "validation", "idempotency", "concurrency", "rate-limiting", "pagination", "filtering", "sorting"]
},
"description": {
"type": "string"
},
"expectedStatusCode": {
"type": "integer"
},
"input": {
"type": "object"
},
"assertions": {
"type": "array",
"items": { "type": "string" }
}
}
},
"contractTestSuite": {
"type": "object",
"description": "Contract testing specifications",
"properties": {
"framework": {
"type": "string",
"enum": ["pact", "spring-cloud-contract", "prism", "custom"]
},
"consumers": {
"type": "array",
"items": {
"$ref": "#/$defs/consumer"
}
},
"providers": {
"type": "array",
"items": {
"$ref": "#/$defs/provider"
}
},
"contracts": {
"type": "array",
"items": {
"$ref": "#/$defs/contract"
}
},
"breakingChanges": {
"type": "array",
"items": {
"$ref": "#/$defs/breakingChange"
}
},
"canIDeploy": {
"type": "boolean",
"description": "Whether deployment is safe based on contract verification"
}
}
},
"consumer": {
"type": "object",
"required": ["name"],
"properties": {
"name": { "type": "string" },
"version": { "type": "string" },
"contractVersion": { "type": "string" }
}
},
"provider": {
"type": "object",
"required": ["name"],
"properties": {
"name": { "type": "string" },
"version": { "type": "string" },
"verificationStatus": {
"type": "string",
"enum": ["passed", "failed", "pending"]
}
}
},
"contract": {
"type": "object",
"description": "Consumer-provider contract",
"required": ["consumer", "provider"],
"properties": {
"consumer": { "type": "string" },
"provider": { "type": "string" },
"interactions": {
"type": "array",
"items": {
"$ref": "#/$defs/interaction"
}
},
"status": {
"type": "string",
"enum": ["verified", "failed", "pending"]
}
}
},
"interaction": {
"type": "object",
"description": "Contract interaction",
"required": ["description", "request", "response"],
"properties": {
"description": { "type": "string" },
"request": {
"type": "object",
"properties": {
"method": { "type": "string" },
"path": { "type": "string" },
"headers": { "type": "object" },
"body": {}
}
},
"response": {
"type": "object",
"properties": {
"status": { "type": "integer" },
"headers": { "type": "object" },
"body": {}
}
}
}
},
"breakingChange": {
"type": "object",
"description": "Breaking change detected in API",
"required": ["type", "description", "severity"],
"properties": {
"type": {
"type": "string",
"enum": ["removed-endpoint", "removed-field", "type-change", "required-field-added", "response-change", "status-code-change"]
},
"description": { "type": "string" },
"severity": {
"type": "string",
"enum": ["critical", "high", "medium", "low"]
},
"affectedConsumers": {
"type": "array",
"items": { "type": "string" }
},
"migrationPath": {
"type": "string"
}
}
},
"integrationTestSuite": {
"type": "object",
"description": "Integration testing specifications",
"properties": {
"framework": {
"type": "string",
"enum": ["supertest", "rest-assured", "playwright", "jest", "mocha", "vitest"]
},
"tests": {
"type": "array",
"items": {
"$ref": "#/$defs/integrationTest"
}
},
"dependencies": {
"type": "array",
"items": { "type": "string" },
"description": "External dependencies required (databases, services)"
}
}
},
"integrationTest": {
"type": "object",
"required": ["name", "endpoint"],
"properties": {
"name": { "type": "string" },
"endpoint": { "$ref": "#/$defs/endpoint" },
"setup": { "type": "string" },
"teardown": { "type": "string" },
"assertions": {
"type": "array",
"items": { "type": "string" }
}
}
},
"componentTestSuite": {
"type": "object",
"description": "Component testing specifications",
"properties": {
"framework": {
"type": "string"
},
"mocks": {
"type": "array",
"items": {
"$ref": "#/$defs/mockConfiguration"
}
},
"tests": {
"type": "array",
"items": {
"$ref": "#/$defs/componentTest"
}
}
}
},
"componentTest": {
"type": "object",
"required": ["name"],
"properties": {
"name": { "type": "string" },
"component": { "type": "string" },
"isolatedDependencies": {
"type": "array",
"items": { "type": "string" }
}
}
},
"loadTestSuite": {
"type": "object",
"description": "Load/performance testing specifications",
"properties": {
"framework": {
"type": "string",
"enum": ["k6", "artillery", "jmeter", "locust", "gatling"]
},
"scenarios": {
"type": "array",
"items": {
"$ref": "#/$defs/loadScenario"
}
},
"thresholds": {
"$ref": "#/$defs/performanceThresholds"
}
}
},
"loadScenario": {
"type": "object",
"required": ["name", "endpoint"],
"properties": {
"name": { "type": "string" },
"endpoint": { "$ref": "#/$defs/endpoint" },
"vus": {
"type": "integer",
"description": "Virtual users"
},
"duration": {
"type": "string",
"description": "Test duration (e.g., '5m', '1h')"
},
"rps": {
"type": "integer",
"description": "Requests per second target"
}
}
},
"performanceThresholds": {
"type": "object",
"properties": {
"p95ResponseTime": {
"type": "integer",
"description": "95th percentile response time in ms"
},
"p99ResponseTime": {
"type": "integer",
"description": "99th percentile response time in ms"
},
"errorRate": {
"type": "number",
"minimum": 0,
"maximum": 100,
"description": "Maximum error rate percentage"
},
"throughput": {
"type": "integer",
"description": "Minimum throughput (requests/second)"
}
}
},
"securityTestSuite": {
"type": "object",
"description": "API security testing specifications",
"properties": {
"authTests": {
"type": "array",
"items": {
"$ref": "#/$defs/authTest"
}
},
"inputValidationTests": {
"type": "array",
"items": {
"$ref": "#/$defs/inputValidationTest"
}
},
"rateLimitingTests": {
"type": "array",
"items": { "type": "object" }
}
}
},
"authTest": {
"type": "object",
"required": ["name", "scenario"],
"properties": {
"name": { "type": "string" },
"scenario": {
"type": "string",
"enum": ["missing-token", "expired-token", "invalid-token", "wrong-scope", "cross-user-access"]
},
"expectedStatusCode": {
"type": "integer"
}
}
},
"inputValidationTest": {
"type": "object",
"required": ["name", "validationType"],
"properties": {
"name": { "type": "string" },
"validationType": {
"type": "string",
"enum": ["missing-required", "wrong-type", "out-of-range", "invalid-format", "sql-injection", "xss"]
},
"expectedStatusCode": {
"type": "integer"
}
}
},
"mockConfiguration": {
"type": "object",
"description": "Mock/stub configuration",
"required": ["name", "type"],
"properties": {
"name": { "type": "string" },
"type": {
"type": "string",
"enum": ["wiremock", "pact", "msw", "nock", "prism", "custom"]
},
"endpoint": { "$ref": "#/$defs/endpoint" },
"responses": {
"type": "array",
"items": {
"type": "object",
"properties": {
"statusCode": { "type": "integer" },
"body": {},
"headers": { "type": "object" }
}
}
}
}
},
"apiFinding": {
"type": "object",
"description": "API testing finding/issue",
"required": ["id", "title", "severity", "category"],
"properties": {
"id": {
"type": "string",
"pattern": "^API-\\d{3,6}$"
},
"title": {
"type": "string",
"minLength": 5,
"maxLength": 200
},
"description": {
"type": "string",
"maxLength": 2000
},
"severity": {
"type": "string",
"enum": ["critical", "high", "medium", "low", "info"]
},
"category": {
"type": "string",
"enum": ["coverage-gap", "contract-violation", "auth-issue", "validation-gap", "performance-risk", "documentation-mismatch"]
},
"endpoint": {
"$ref": "#/$defs/endpoint"
},
"remediation": {
"type": "string"
}
}
},
"recommendation": {
"type": "object",
"required": ["id", "title", "priority"],
"properties": {
"id": {
"type": "string",
"pattern": "^REC-\\d{3,6}$"
},
"title": {
"type": "string",
"maxLength": 200
},
"description": {
"type": "string",
"maxLength": 2000
},
"priority": {
"type": "string",
"enum": ["critical", "high", "medium", "low"]
},
"effort": {
"type": "string",
"enum": ["trivial", "low", "medium", "high", "major"]
},
"codeExample": {
"type": "string",
"maxLength": 5000
}
}
},
"apiMetrics": {
"type": "object",
"description": "API testing metrics",
"properties": {
"totalEndpoints": { "type": "integer", "minimum": 0 },
"coveredEndpoints": { "type": "integer", "minimum": 0 },
"contractsCovered": { "type": "integer", "minimum": 0 },
"integrationTestCount": { "type": "integer", "minimum": 0 },
"componentTestCount": { "type": "integer", "minimum": 0 },
"loadTestCount": { "type": "integer", "minimum": 0 },
"securityTestCount": { "type": "integer", "minimum": 0 },
"coveragePercentage": {
"type": "number",
"minimum": 0,
"maximum": 100
},
"duration": { "type": "integer", "minimum": 0 }
}
},
"categoryScore": {
"type": "object",
"required": ["score"],
"properties": {
"score": {
"type": "number",
"minimum": 0,
"maximum": 100
},
"weight": {
"type": "number",
"minimum": 0,
"maximum": 1
},
"description": {
"type": "string"
},
"grade": {
"type": "string",
"pattern": "^[A-F][+-]?$"
}
}
}
}
}
{
"skillName": "api-testing-patterns",
"skillVersion": "1.0.0",
"requiredTools": [
"jq"
],
"optionalTools": [
"node",
"supertest",
"pact",
"ajv",
"jsonschema",
"python3"
],
"schemaPath": "schemas/output.json",
"requiredFields": [
"skillName",
"status",
"output",
"output.testingLevel",
"output.endpointCoverage"
],
"requiredNonEmptyFields": [
"output.summary",
"output.testingLevel"
],
"mustContainTerms": [
"api",
"test"
],
"mustNotContainTerms": [
"TODO",
"FIXME",
"placeholder",
"example.com",
"lorem ipsum"
],
"enumValidations": {
".status": [
"success",
"partial",
"failed",
"skipped"
],
".output.testingLevel": [
"contract",
"component",
"integration",
"e2e",
"load",
"mixed"
],
".output.apiType": [
"rest",
"graphql",
"grpc",
"websocket",
"mixed"
]
}
}
API Test Scaffold Template
REST API Test Structure (Jest/Supertest)
import request from 'supertest';
import { app } from '../src/app';
describe('{{Resource}} API', () => {
// Setup
let authToken: string;
beforeAll(async () => {
authToken = await getTestToken();
});
describe('GET /api/{{resource}}', () => {
it('returns paginated list with valid auth', async () => {
const res = await request(app)
.get('/api/{{resource}}')
.set('Authorization', `Bearer ${authToken}`)
.query({ page: 1, limit: 10 });
expect(res.status).toBe(200);
expect(res.body.data).toBeInstanceOf(Array);
expect(res.body.pagination).toMatchObject({
page: 1,
limit: 10,
total: expect.any(Number)
});
});
it('returns 401 without auth', async () => {
const res = await request(app).get('/api/{{resource}}');
expect(res.status).toBe(401);
});
it('returns 400 for invalid query params', async () => {
const res = await request(app)
.get('/api/{{resource}}')
.set('Authorization', `Bearer ${authToken}`)
.query({ page: -1 });
expect(res.status).toBe(400);
});
});
describe('POST /api/{{resource}}', () => {
it('creates resource with valid payload', async () => {
const payload = { /* valid fields */ };
const res = await request(app)
.post('/api/{{resource}}')
.set('Authorization', `Bearer ${authToken}`)
.send(payload);
expect(res.status).toBe(201);
expect(res.body.id).toBeDefined();
});
it('returns 422 for invalid payload', async () => {
const res = await request(app)
.post('/api/{{resource}}')
.set('Authorization', `Bearer ${authToken}`)
.send({});
expect(res.status).toBe(422);
expect(res.body.errors).toBeDefined();
});
it('is idempotent with same request ID', async () => {
const requestId = crypto.randomUUID();
const payload = { /* valid fields */ };
const res1 = await request(app)
.post('/api/{{resource}}')
.set('Authorization', `Bearer ${authToken}`)
.set('X-Request-ID', requestId)
.send(payload);
const res2 = await request(app)
.post('/api/{{resource}}')
.set('Authorization', `Bearer ${authToken}`)
.set('X-Request-ID', requestId)
.send(payload);
expect(res1.body.id).toBe(res2.body.id);
});
});
});Related skills
How it compares
Pick api-testing-patterns over pytest-patterns when the target is HTTP REST or GraphQL contract and integration coverage—not Python unit test layout.
FAQ
What API styles does api-testing-patterns support?
api-testing-patterns supports REST and GraphQL services with contract, integration, and negative-path suites. The skill emphasizes auth, pagination, idempotency, and error-shape assertions before release.
What problems does api-testing-patterns solve?
api-testing-patterns helps developers move beyond happy-path smoke tests to systematic API coverage. The skill produces test scenarios for auth failures, pagination edges, idempotent retries, and structured error responses.