api-testing

Master API testing strategies and automation to ensure your APIs are reliable, secure, and performant.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "api-testing" with this command: npx skills add karchtho/my-claude-marketplace/karchtho-my-claude-marketplace-api-testing

API Testing

Master API testing strategies and automation to ensure your APIs are reliable, secure, and performant.

When to Use This Skill

  • Setting up API testing infrastructure

  • Integrating Postman MCP for automation

  • Writing unit and integration tests for endpoints

  • Implementing contract testing and validation

  • Setting up CI/CD pipeline testing

  • Testing GraphQL queries and mutations

  • Automating API test execution

  • Building test suites with multiple frameworks

Testing Pyramid for APIs

Structure your tests from bottom to top:

    E2E Tests (Postman collections, full flow)
   /                  \
  /                    \

Integration Tests API Tests (DB, caching, etc.) (endpoint behavior) /
/
Unit Tests (business logic, validation)

  1. Unit Tests (Foundation)

Test individual functions and business logic in isolation:

pytest example

import pytest from app.models import User

def test_user_password_hashing(): user = User(email="test@example.com") user.set_password("mypassword") assert user.password != "mypassword" assert user.check_password("mypassword") is True assert user.check_password("wrongpassword") is False

def test_user_email_validation(): with pytest.raises(ValueError): User(email="invalid-email")

  1. Integration Tests

Test API endpoints with real dependencies (database, caching):

pytest example with fixtures

import pytest from fastapi.testclient import TestClient from app import app from app.db import get_db

@pytest.fixture def client(db_session): def override_get_db(): return db_session app.dependency_overrides[get_db] = override_get_db return TestClient(app)

def test_create_user(client): response = client.post("/api/users", json={ "email": "newuser@example.com", "name": "New User" }) assert response.status_code == 201 assert response.json()["email"] == "newuser@example.com"

def test_get_user(client, db_session): # Create test user from app.models import User user = User(email="test@example.com", name="Test") db_session.add(user) db_session.commit()

response = client.get(f"/api/users/{user.id}")
assert response.status_code == 200
assert response.json()["name"] == "Test"

3. End-to-End Tests

Test full user workflows using Postman collections:

  • Import from Postman collections

  • Run with Newman CLI

  • Execute in CI/CD pipelines

  • Test realistic user scenarios

REST API Testing with Pytest

Basic Endpoint Testing

import pytest from fastapi.testclient import TestClient from app import app

client = TestClient(app)

class TestUserAPI: def test_list_users(self): response = client.get("/api/users") assert response.status_code == 200 assert isinstance(response.json(), list)

def test_create_user_success(self):
    response = client.post("/api/users", json={
        "email": "newuser@example.com",
        "name": "New User",
        "password": "secure123"
    })
    assert response.status_code == 201
    data = response.json()
    assert data["email"] == "newuser@example.com"
    assert "password" not in data  # Never expose passwords

def test_create_user_validation_error(self):
    response = client.post("/api/users", json={
        "email": "invalid-email",
        "name": "Test"
    })
    assert response.status_code == 422
    assert "email" in response.json()["detail"][0]["loc"]

def test_get_nonexistent_user(self):
    response = client.get("/api/users/99999")
    assert response.status_code == 404

def test_update_user(self):
    # Create user first
    create_resp = client.post("/api/users", json={
        "email": "user@example.com",
        "name": "Original"
    })
    user_id = create_resp.json()["id"]

    # Update user
    response = client.patch(f"/api/users/{user_id}", json={
        "name": "Updated"
    })
    assert response.status_code == 200
    assert response.json()["name"] == "Updated"

def test_delete_user(self):
    # Create user
    create_resp = client.post("/api/users", json={
        "email": "deleteme@example.com",
        "name": "Delete Me"
    })
    user_id = create_resp.json()["id"]

    # Delete
    response = client.delete(f"/api/users/{user_id}")
    assert response.status_code == 204

    # Verify deletion
    get_resp = client.get(f"/api/users/{user_id}")
    assert get_resp.status_code == 404

Parametrized Testing

import pytest

@pytest.mark.parametrize("status_code,expected_status", [ (200, 200), (201, 201), (400, 400), (404, 404), ]) def test_status_codes(status_code, expected_status): response = client.get(f"/api/test?status={status_code}") assert response.status_code == expected_status

GraphQL Testing with Jest

Basic Query Testing

import { gql } from '@apollo/client'; import { ApolloClient } from '@apollo/client';

const client = new ApolloClient({ uri: 'http://localhost:4000/graphql' });

describe('GraphQL User Queries', () => { it('should fetch user by ID', async () => { const query = gql query GetUser($id: ID!) { user(id: $id) { id name email } } ;

const result = await client.query({
  query,
  variables: { id: '1' }
});

expect(result.data.user).toBeDefined();
expect(result.data.user.email).toBe('user@example.com');

});

it('should list users with pagination', async () => { const query = gql query ListUsers($first: Int!) { users(first: $first) { edges { node { id name } } pageInfo { hasNextPage } } } ;

const result = await client.query({
  query,
  variables: { first: 10 }
});

expect(result.data.users.edges).toHaveLength(10);
expect(result.data.users.pageInfo.hasNextPage).toBeDefined();

}); });

describe('GraphQL Mutations', () => { it('should create user with validation', async () => { const mutation = gql mutation CreateUser($input: CreateUserInput!) { createUser(input: $input) { user { id email } errors { field message } success } } ;

const result = await client.mutate({
  mutation,
  variables: {
    input: { email: 'newuser@example.com', name: 'New' }
  }
});

expect(result.data.createUser.success).toBe(true);
expect(result.data.createUser.user.id).toBeDefined();
expect(result.data.createUser.errors).toHaveLength(0);

});

it('should return errors for invalid input', async () => { const mutation = gql mutation CreateUser($input: CreateUserInput!) { createUser(input: $input) { user { id } errors { field message } success } } ;

const result = await client.mutate({
  mutation,
  variables: {
    input: { email: 'invalid-email', name: '' }
  }
});

expect(result.data.createUser.success).toBe(false);
expect(result.data.createUser.errors.length).toBeGreaterThan(0);

}); });

Postman MCP Integration

Installation

The Postman MCP server enables running Postman collections through Claude Code:

Method 1: Via Smithery (Recommended)

npx -y @smithery/cli install mcp-postman --client claude

Method 2: Manual Installation

git clone https://github.com/shannonlal/mcp-postman.git cd mcp-postman pnpm install pnpm build

Configuration

Add to ~/.config/claude/config.json :

{ "mcpServers": { "postman": { "command": "node", "args": ["/path/to/mcp-postman/build/index.js"], "env": { "POSTMAN_API_KEY": "your-api-key-here" } } } }

Using Postman MCP

Once configured, interact with Postman from Claude Code:

User: "Run the user-api collection from my Postman workspace"

Claude can now:

  • List available collections
  • Execute collections with specific environments
  • Parse test results
  • Run in CI/CD pipelines
  • Generate test reports

Newman Automation

Newman CLI runs Postman collections headlessly for CI/CD:

Installation

npm install -g newman npm install -g newman-reporter-html

Running Collections

Basic execution

newman run collection.json

With environment

newman run collection.json -e environment.json

With variables

newman run collection.json --global-var "base_url=https://api.example.com"

Multiple reporters

newman run collection.json
--reporters cli,json,html
--reporter-json-export results.json
--reporter-html-export report.html

CI/CD Integration Example

GitHub Actions:

name: API Tests on: [push, pull_request]

jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 - run: npm install -g newman - run: | newman run postman/collection.json
-e postman/environment.json
--reporters cli,json
--reporter-json-export results.json

Contract Testing

Validate APIs match their specifications:

OpenAPI Validation

Using openapi-spec-validator

from openapi_spec_validator import validate_spec import json

with open('openapi.json') as f: spec = json.load(f)

try: validate_spec(spec) print("OpenAPI spec is valid") except Exception as e: print(f"Spec validation error: {e}")

Pact Consumer/Provider Testing

// Consumer test with Pact import { Pact } from '@pact-foundation/pact';

describe('User Consumer', () => { const provider = new Pact({ consumer: 'UserApp', provider: 'UserAPI' });

it('should fetch user', () => { return provider .addInteraction({ state: 'user exists', uponReceiving: 'a request for user 123', withRequest: { method: 'GET', path: '/api/users/123' }, willRespondWith: { status: 200, body: { id: 123, name: 'John', email: 'john@example.com' } } }) .then(() => fetch('http://localhost:8080/api/users/123')) .then(response => response.json()) .then(data => expect(data.email).toBe('john@example.com')); }); });

Authentication Testing

def test_bearer_token_required(client): response = client.get("/api/users") assert response.status_code == 401

def test_valid_bearer_token(client): headers = {"Authorization": "Bearer valid-token-123"} response = client.get("/api/users", headers=headers) assert response.status_code == 200

def test_invalid_bearer_token(client): headers = {"Authorization": "Bearer invalid-token"} response = client.get("/api/users", headers=headers) assert response.status_code == 401

def test_api_key_validation(client): headers = {"X-API-Key": "valid-key"} response = client.get("/api/users", headers=headers) assert response.status_code == 200

headers = {"X-API-Key": "invalid-key"}
response = client.get("/api/users", headers=headers)
assert response.status_code == 401

Performance Testing

Load testing with locust

from locust import HttpUser, task, constant

class APIUser(HttpUser): wait_time = constant(1)

@task
def list_users(self):
    self.client.get("/api/users")

@task
def get_user(self):
    self.client.get("/api/users/1")

Run with: locust -f locustfile.py -u 100 -r 10

Best Practices

  • Test at Multiple Levels: Unit → Integration → E2E

  • Use Test Fixtures: Reusable setup/teardown for consistent tests

  • Parametrize Tests: Test multiple scenarios efficiently

  • Mock External APIs: Isolate your tests from dependencies

  • Automate in CI/CD: Run tests on every commit

  • Document Test Cases: Clear test names and docstrings

  • Test Error Cases: Not just happy paths

  • Use Postman MCP: Automate Postman collection execution

  • Monitor Test Coverage: Aim for >80% coverage

  • Test Performance: Regular load and performance tests

Common Pitfalls to Avoid

  • Testing implementation details instead of behavior

  • Flaky tests that fail intermittently

  • Not testing error responses

  • Ignoring authentication/authorization tests

  • Missing edge cases and boundary conditions

  • Slow test suites (optimize or split)

  • Testing external APIs without mocking

  • No test data management strategy

Cross-Skill References

  • rest-api-design skill - For REST endpoint patterns to test

  • graphql-api-design skill - For GraphQL query patterns to test

  • api-architecture skill - For security and monitoring strategies

Reference files for this skill are planned for a future release.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

nginx-configuration

No summary provided by upstream source.

Repository SourceNeeds Review
General

docker-container-basics

No summary provided by upstream source.

Repository SourceNeeds Review
General

dockerfile-generator

No summary provided by upstream source.

Repository SourceNeeds Review
General

docker-compose-creator

No summary provided by upstream source.

Repository SourceNeeds Review