Skip to content
Testing
Skill

/api-testing-rest

Comprehensive RESTful API testing patterns covering HTTP methods, status codes, request/response validation, authentication, error handling, and contract testing.

From plugin
qaskills
24413 skills

Claude Code

$ npx -y skills add PramodDutta/qaskills --skill api-testing-rest --agent claude-code

SKILL.md

api-testing-rest.SKILL.md
name: api-testing-rest
description: Comprehensive RESTful API testing patterns covering HTTP methods, status codes, request/response validation, authentication, error handling, and contract testing.
license: MIT
metadata:
  author: thetestingacademy
  version: 1.0.0
  source: https://qaskills.sh/skills/thetestingacademy/api-testing-rest

API Testing REST Skill

You are an expert QA engineer specializing in REST API testing. When the user asks you to write, review, or design API tests, follow these detailed instructions.

Core Principles

1. **Test the contract, not the implementation** -- Focus on request/response format, not server internals. 2. **Cover all HTTP methods** -- GET, POST, PUT, PATCH, DELETE each have different semantics. 3. **Validate status codes** -- Correct status codes are part of the API contract. 4. **Test error paths** -- Bad requests and edge cases are as important as happy paths. 5. **Assert on response structure** -- JSON schema validation ensures consistency.

REST API Fundamentals

HTTP Methods and Their Semantics

GET     - Retrieve resource(s), safe and idempotent
POST    - Create new resource, not idempotent
PUT     - Replace entire resource, idempotent
PATCH   - Partial update, idempotent
DELETE  - Remove resource, idempotent
HEAD    - Same as GET but no response body
OPTIONS - Get supported methods for resource

HTTP Status Codes

Success (2xx):
  200 OK              - Successful GET, PUT, PATCH, DELETE
  201 Created         - Successful POST, resource created
  204 No Content      - Successful DELETE (no body returned)

Client Error (4xx):
  400 Bad Request     - Invalid request body or parameters
  401 Unauthorized    - Missing or invalid authentication
  403 Forbidden       - Authenticated but not authorized
  404 Not Found       - Resource doesn't exist
  409 Conflict        - Resource conflict (duplicate email)
  422 Unprocessable   - Validation error

Server Error (5xx):
  500 Internal Error  - Server error
  503 Service Unavailable - Service down or overloaded

Testing Patterns with Different Tools

1. JavaScript/TypeScript with Axios/Fetch

// api-client.ts
import axios from 'axios';

export class ApiClient {
  private baseURL = 'https://api.example.com';
  private authToken: string | null = null;

  setAuthToken(token: string) {
    this.authToken = token;
  }

  private getHeaders() {
    return {
      'Content-Type': 'application/json',
      ...(this.authToken && { Authorization: `Bearer ${this.authToken}` }),
    };
  }

  async get(endpoint: string, params = {}) {
    const response = await axios.get(`${this.baseURL}${endpoint}`, {
      headers: this.getHeaders(),
      params,
    });
    return response;
  }

  async post(endpoint: string, data: any) {
    const response = await axios.post(`${this.baseURL}${endpoint}`, data, {
      headers: this.getHeaders(),
    });
    return response;
  }

  async put(endpoint: string, data: any) {
    const response = await axios.put(`${this.baseURL}${endpoint}`, data, {
      headers: this.getHeaders(),
    });
    return response;
  }

  async delete(endpoint: string) {
    const response = await axios.delete(`${this.baseURL}${endpoint}`, {
      headers: this.getHeaders(),
    });
    return response;
  }
}
// users.api.test.ts
import { describe, it, expect, beforeAll } from 'vitest';
import { ApiClient } from './api-client';

describe('Users API', () => {
  const api = new ApiClient();
  let createdUserId: string;

  beforeAll(async () => {
    // Authenticate before running tests
    const authResponse = await api.post('/auth/login', {
      email: 'test@example.com',
      password: 'password123',
    });
    api.setAuthToken(authResponse.data.token);
  });

  describe('POST /api/users', () => {
    it('should create a new user', async () => {
      const userData = {
        email: 'newuser@example.com',
        name: 'New User',
        role: 'user',
      };

      const response = await api.post('/api/users', userData);

      // Assert status code
      expect(response.status).toBe(201);

      // Assert response structure
      expect(response.data).toHaveProperty('id');
      expect(response.data).toHaveProperty('email', userData.email);
      expect(response.data).toHaveProperty('name', userData.name);
      expect(response.data).toHaveProperty('createdAt');

      // Assert response types
      expect(typeof response.data.id).toBe('string');
      expect(response.data.createdAt).toMatch(/^\d{4}-\d{2}-\d{2}T/);

      // Save for cleanup
      createdUserId = response.data.id;
    });

    it('should return 400 for invalid email', async () => {
      try {
        await api.post('/api/users', {
          email: 'invalid-email',
          name: 'Test',
        });
        fail('Should have thrown an error');
      } catch (error: any) {
        expect(error.response.status).toBe(400);
        expect(error.response.data).toHaveProperty('error');
        expect(error.response.data.error).toContain('email');
      }
    });

    it('should return 409 for duplicate email', async () => {
      const userData = {
        email: 'duplicate@example.com',
        name: 'Duplicate User',
      };

      // Create first user
      await api.post('/api/users', userData);

      // Attempt to create duplicate
      try {
        await api.post('/api/users', userData);
        fail('Should have thrown an error');
      } catch (error: any) {
        expect(error.response.status).toBe(409);
        expect(error.response.data.error).toContain('already exists');
      }
    });
  });

  describe('GET /api/users/:id', () => {
    it('should retrieve user by ID', async () => {
      const response = await api.get(`/api/users/${createdUserId}`);

      expect(response.status).toBe(200);
      expect(response.data.id).toBe(createdUserId);
      expect(response.data).toHaveProperty('email');
      expect(response.data).toHavePrope
Read more
@pramoddutta
Ships withqaskills

QA Skills Directory QA Skills is a curated directory of testing-specific skills for AI coding agents (Claude Code, Cursor, Copilot, etc.).

Stars on GitHub

Momentum

stars in total
244
Star history of PramodDutta/qaskills on GitHub, a chart by star-history.com

Other skills on qaskills.

add-seed-skills

add-seed-skills

Use when adding or editing QA skills in seed-skills/ or getting them onto the live qaskills.sh catalog, e.g. "add N new skills",…

publish-seo-batch

publish-seo-batch

Use when publishing SEO blog articles to qaskills.sh, e.g. "publish today's articles", "daily SEO batch", "write 10 articles from…

ship-prod

ship-prod

Use when deploying qaskills.sh to production, verifying whether a deploy landed, or when a push to main did not show up on the…

claude-code-qa

claude-code-qa

The complete QA skill for Claude Code — turn Claude into an expert QA engineer that picks the right test type, writes reliable…

cypress-e2e

cypress-e2e

End-to-end testing skill using Cypress for web applications, covering custom commands, network intercepts, fixtures, cy.session,…

e2e-testing-claude-code

e2e-testing-claude-cod…

Make Claude Code write and maintain end-to-end tests like a senior SDET — Playwright and Cypress flows with stable locators, the…

More skills from other plugins.

api-review

api-review

Produce the "Playwright 1.xx API Review" artifact for a range of commits - the public API additions and the breaking or behavior…

@microsoft@microsoftInstall Skill
add-e2e-selectors

add-e2e-selectors

Add reliable @grafana/e2e-selectors to interactive elements and key containers in the Grafana frontend. Use when adding e2e…

grafana1d78k
@grafana@grafanaInstall Skill
promptfoo-evals

promptfoo-evals

Write, refine, run, and QA promptfoo evaluation suites: promptfooconfig.yaml, prompts, providers, vars, tests, assertions,…

@promptfoo@promptfooInstall Skill
deepeval

deepeval

DeepEval evaluation workflow for AI agents and LLM applications. TRIGGER when the user wants to evaluate or improve an AI agent,…

@confident-ai@confident-aiInstall Skill

Questions about api-testing-rest.

Which coding agents can use api-testing-rest?
api-testing-rest installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.