Skip to main content

api-testing-rest

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

Quellinformationen

Repository
PramodDutta/qaskills
Letzte Quellaktivität
3. Juli 2026 um 08:00
Erkannte Sprache von SKILL.md
Englisch
Sterne
244
Forks
32

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
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 ```typescript // 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; } } ``` ```typescript // 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).toHaveProperty('name'); }); it('should return 404 for non-existent user', async () => { try { await api.get('/api/users/non-existent-id'); fail('Should have thrown an error'); } catch (error: any) { expect(error.response.status).toBe(404); } }); }); describe('GET /api/users', () => { it('should list all users', async () => { const response = await api.get('/api/users'); expect(response.status).toBe(200); expect(Array.isArray(response.data)).toBe(true); expect(response.data.length).toBeGreaterThan(0); // Validate structure of first user const firstUser = response.data[0]; expect(firstUser).toHaveProperty('id'); expect(firstUser).toHaveProperty('email'); expect(firstUser).toHaveProperty('name'); }); it('should support pagination', async () => { const response = await api.get('/api/users', { page: 1, limit: 10, }); expect(response.status).toBe(200); expect(response.data).toHaveProperty('items'); expect(response.data).toHaveProperty('total'); expect(response.data).toHaveProperty('page', 1); expect(response.data).toHaveProperty('limit', 10); expect(response.data.items.length).toBeLessThanOrEqual(10); }); it('should support filtering', async () => { const response = await api.get('/api/users', { role: 'admin', }); expect(response.status).toBe(200); expect(Array.isArray(response.data)).toBe(true); // All users should be admins response.data.forEach((user: any) => { expect(user.role).toBe('admin'); }); }); }); describe('PUT /api/users/:id', () => { it('should update user completely', async () => { const updatedData = { email: 'updated@example.com', name: 'Updated Name', role: 'admin', }; const response = await api.put(`/api/users/${createdUserId}`, updatedData); expect(response.status).toBe(200); expect(response.data.email).toBe(updatedData.email); expect(response.data.name).toBe(updatedData.name); expect(response.data.role).toBe(updatedData.role); }); it('should return 404 for non-existent user', async () => { try { await api.put('/api/users/non-existent', { name: 'Test' }); fail('Should have thrown an error'); } catch (error: any) { expect(error.response.status).toBe(404); } }); }); describe('DELETE /api/users/:id', () => { it('should delete user', async () => { const response = await api.delete(`/api/users/${createdUserId}`); expect(response.status).toBe(204); // Verify deletion try { await api.get(`/api/users/${createdUserId}`); fail('User should be deleted'); } catch (error: any) { expect(error.response.status).toBe(404); } }); it('should return 404 when deleting non-existent user', async () => { try { await api.delete('/api/users/non-existent'); fail('Should have thrown an error'); } catch (error: any) { expect(error.response.status).toBe(404); } }); }); }); ``` ### 2. Python with requests/pytest ```python # api_client.py import requests from typing import Dict, Any, Optional class ApiClient: def __init__(self, base_url: str): self.base_url = base_url self.session = requests.Session() self.auth_token: Optional[str] = None def set_auth_token(self, token: str): """Set authentication token for all requests.""" self.auth_token = token self.session.headers.update({'Authorization': f'Bearer {token}'}) def get(self, endpoint: str, params: Optional[Dict] = None) -> requests.Response: """Perform GET request.""" url = f"{self.base_url}{endpoint}" return self.session.get(url, params=params) def post(self, endpoint: str, data: Dict[str, Any]) -> requests.Response: """Perform POST request.""" url = f"{self.base_url}{endpoint}" return self.session.post(url, json=data) def put(self, endpoint: str, data: Dict[str, Any]) -> requests.Response: """Perform PUT request.""" url = f"{self.base_url}{endpoint}" return self.session.put(url, json=data) def delete(self, endpoint: str) -> requests.Response: """Perform DELETE request.""" url = f"{self.base_url}{endpoint}" return self.session.delete(url) ``` ```python # test_users_api.py import pytest from api_client import ApiClient @pytest.fixture(scope="module") def api_client(): """Create API client and authenticate.""" client = ApiClient("https://api.example.com") # Authenticate response = client.post("/auth/login", { "email": "test@example.com", "password": "password123" }) assert response.status_code == 200 client.set_auth_token(response.json()["token"]) return client @pytest.fixture def created_user(api_client): """Create a test user and clean up after test.""" response = api_client.post("/api/users", {
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen