Get the FREE Ultimate OpenClaw Setup Guide →

api-testing

Scanned
npx machina-cli add skill secondsky/claude-skills/api-testing --openclaw
Files (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 describe blocks
  • 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 framework
  • playwright-testing - E2E API testing
  • test-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

  1. Step 1: Install dependencies for both stacks (TypeScript: supertest and vitest; Python: httpx and pytest-asyncio)
  2. Step 2: Create a minimal test file that exercises a health endpoint and a create-user flow like the examples
  3. 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
Sponsor this space

Reach thousands of developers