api-testing
Scannednpx machina-cli add skill secondsky/claude-skills/api-testing --openclawFiles (1)
SKILL.md
5.5 KB
API Testing
Expert knowledge for testing HTTP APIs with Supertest (TypeScript/JavaScript) and httpx/pytest (Python).
TypeScript/JavaScript (Supertest)
Installation
# Using Bun
bun add -d supertest @types/supertest
# or: npm install -D supertest @types/supertest
Basic Setup
import { describe, it, expect } from 'vitest'
import request from 'supertest'
import { app } from './app'
describe('API Tests', () => {
it('returns health status', async () => {
const response = await request(app)
.get('/api/health')
.expect(200)
expect(response.body).toEqual({ status: 'ok' })
})
it('creates a user', async () => {
const response = await request(app)
.post('/api/users')
.send({ name: 'John Doe', email: 'john@example.com' })
.expect(201)
expect(response.body).toMatchObject({
id: expect.any(Number),
name: 'John Doe',
})
})
it('validates required fields', async () => {
await request(app)
.post('/api/users')
.send({ name: 'John Doe' })
.expect(400)
})
})
Request Methods
// GET
await request(app).get('/api/users').expect(200)
// POST with body
await request(app)
.post('/api/users')
.send({ name: 'John' })
.expect(201)
// PUT
await request(app)
.put('/api/users/1')
.send({ name: 'Jane' })
.expect(200)
// DELETE
await request(app).delete('/api/users/1').expect(204)
Headers and Query Parameters
// Set headers
await request(app)
.get('/api/protected')
.set('Authorization', 'Bearer token123')
.expect(200)
// Query parameters
await request(app)
.get('/api/users')
.query({ page: 1, limit: 10 })
.expect(200)
Authentication Testing
describe('Authentication', () => {
let authToken: string
beforeAll(async () => {
const response = await request(app)
.post('/api/auth/login')
.send({ email: 'user@example.com', password: 'password123' })
.expect(200)
authToken = response.body.token
})
it('accesses protected endpoint', async () => {
await request(app)
.get('/api/protected')
.set('Authorization', `Bearer ${authToken}`)
.expect(200)
})
it('rejects without token', async () => {
await request(app).get('/api/protected').expect(401)
})
})
Error Handling
it('handles validation errors', async () => {
const response = await request(app)
.post('/api/users')
.send({ email: 'invalid-email' })
.expect(400)
expect(response.body).toMatchObject({
error: 'Validation failed',
details: expect.any(Array),
})
})
it('handles not found', async () => {
await request(app).get('/api/users/999999').expect(404)
})
Python (httpx + pytest)
Installation
uv add --dev httpx pytest-asyncio
Basic Setup
import pytest
from fastapi.testclient import TestClient
from main import app
client = TestClient(app)
def test_health_check():
response = client.get("/api/health")
assert response.status_code == 200
assert response.json() == {"status": "ok"}
def test_create_user():
response = client.post(
"/api/users",
json={"name": "John Doe", "email": "john@example.com"}
)
assert response.status_code == 201
data = response.json()
assert data["name"] == "John Doe"
assert "id" in data
def test_not_found():
response = client.get("/api/users/999")
assert response.status_code == 404
Fixtures
@pytest.fixture
def auth_token(client):
response = client.post(
"/api/auth/login",
json={"email": "user@example.com", "password": "password123"}
)
return response.json()["token"]
def test_protected_endpoint(client, auth_token):
response = client.get(
"/api/protected",
headers={"Authorization": f"Bearer {auth_token}"}
)
assert response.status_code == 200
File Upload
def test_file_upload(client, tmp_path):
test_file = tmp_path / "test.txt"
test_file.write_text("test content")
with open(test_file, "rb") as f:
response = client.post(
"/api/upload",
files={"file": ("test.txt", f, "text/plain")}
)
assert response.status_code == 200
GraphQL Testing
it('queries GraphQL endpoint', async () => {
const query = `
query GetUser($id: ID!) {
user(id: $id) { id name email }
}
`
const response = await request(app)
.post('/graphql')
.send({ query, variables: { id: '1' } })
.expect(200)
expect(response.body.data.user).toMatchObject({
id: '1',
name: expect.any(String),
})
})
Performance Testing
it('responds within acceptable time', async () => {
const start = Date.now()
await request(app).get('/api/users').expect(200)
const duration = Date.now() - start
expect(duration).toBeLessThan(100) // 100ms threshold
})
Best Practices
- Group related endpoints in
describeblocks - Reset database between tests
- Validate status codes first
- Check response structure
- Test error message format
- Mock external services
- Test both happy path and error cases
See Also
vitest-testing- Unit testing frameworkplaywright-testing- E2E API testingtest-quality-analysis- Test quality patterns
Source
git clone https://github.com/secondsky/claude-skills/blob/main/plugins/api-testing/skills/api-testing/SKILL.mdView on GitHub Overview
This skill teaches end-to-end HTTP API testing using Supertest for TypeScript/JavaScript and httpx with pytest-asyncio in Python. It covers REST APIs, GraphQL, request/response validation, authentication flows, and robust error handling.
How This Skill Works
Tests are written against your app using Supertest to assert status codes and payloads in TypeScript/JavaScript, and with httpx via pytest-asyncio in Python to exercise FastAPI/ASGI apps. The examples demonstrate common patterns: health checks, resource creation, authentication, error scenarios, and protected endpoints with tokens.
When to Use It
- Contract/API health checks for new or updated endpoints
- Validate authentication flows and protected routes
- Verify validation and error handling responses
- Test REST and GraphQL endpoints across languages
- Regression tests for critical user journeys
Quick Start
- Step 1: Install dependencies for both stacks (TypeScript: supertest and vitest; Python: httpx and pytest-asyncio)
- Step 2: Create a minimal test file that exercises a health endpoint and a create-user flow like the examples
- Step 3: Run tests (TS/JS: npm run test or bun test; Python: pytest)
Best Practices
- Assert explicit status codes and response shapes for every test
- Test both positive and negative scenarios (success and failures)
- Reuse test clients and fixtures to keep tests fast and deterministic
- Centralize authentication setup (e.g., login to obtain tokens) in fixtures
- Isolate tests per endpoint and avoid relying on shared state
Example Use Cases
- Health check: GET /api/health returns 200 and { status: 'ok' }
- Create user: POST /api/users with name/email returns 201 and an id
- Validation error: POST /api/users with invalid data returns 400
- Authenticated access: GET /api/protected with a valid Bearer token returns 200
- Not found: GET /api/users/999999 returns 404
Frequently Asked Questions
Add this skill to your agents