/api-testing-rest
Comprehensive RESTful API testing patterns covering HTTP methods, status codes, request/response validation, authentication, error handling, and contract testing.
$ npx -y skills add PramodDutta/qaskills --skill api-testing-rest --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/api-testing-rest
Context preview
The summary Claude sees to decide when to auto-load this skill.
Comprehensive RESTful API testing patterns covering HTTP methods, status codes, request/response validation, authentication, error handling, and contract testing.
SKILL.md
api-testing-rest.SKILL.mdname: 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).toHavePropeRead more
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).toHavePropeQA Skills Directory QA Skills is a curated directory of testing-specific skills for AI coding agents (Claude Code, Cursor, Copilot, etc.).
Repo: PramodDutta/qaskills
Other skills on qaskills.
- /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", "create a seed skill for X", "seed the database", "the skill page is empty", "skill 404s on the site".
Open skill - /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 keyword research", "add a blog post", or any request that creates files under packages/web/src/app/blog/posts.
Open skill - /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 live site, e.g. "deploy", "ship it", "push this live", "is prod updated?", "the site still shows the old version".
Open skill - /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 Playwright, Cypress, and pytest tests, eliminates flaky tests, enforces coverage, and wires up CI. Claude Code QA testing done right.
Open skill - /cypress-e2e
End-to-end testing skill using Cypress for web applications, covering custom commands, network intercepts, fixtures, cy.session, and component testing patterns.
Open skill - /e2e-testing-claude-code
Make Claude Code write and maintain end-to-end tests like a senior SDET — Playwright and Cypress flows with stable locators, the Page Object Model, fixtures, reused auth state, network mocking, and flake-free CI. Claude Code E2E testing, done right.
Open skill

