DevToolBoxKOSTENLOS
Blog

API-Tests: Kompletter Leitfaden mit cURL, Supertest und k6

13 Min. Lesezeitvon DevToolBox

TL;DR

API testing spans multiple layers: manual exploration with cURL and Postman, automated integration tests with supertest (Node.js) or httpx + pytest (Python), mock servers with MSW or json-server for frontend development, and load testing with k6 or Artillery. Document your API with OpenAPI 3.1 and enforce contracts with Pact. Convert any cURL command to code in your language with our cURL to code converter.

HTTP Methods and Status Codes — The Foundation of API Testing

Understanding HTTP semantics is the prerequisite for effective API testing. Every test makes assumptions about idempotency, safety, and expected status codes.

HTTP Method Semantics

MethodPurposeIdempotent?Safe?Typical Success Status
GETRetrieve resourceYesYes200 OK
POSTCreate resource or trigger actionNoNo201 Created
PUTReplace full resourceYesNo200 OK or 204
PATCHPartial updateDependsNo200 OK or 204
DELETERemove resourceYesNo204 No Content

HTTP Status Code Reference

CodeNameWhen to Use
200OKSuccessful GET, PUT, PATCH
201CreatedSuccessful POST that created a resource; include Location header
204No ContentSuccessful DELETE or PUT/PATCH with no response body
400Bad RequestInvalid input, malformed JSON, missing required fields
401UnauthorizedMissing or invalid authentication credentials
403ForbiddenAuthenticated but lacks permission for this action
404Not FoundResource does not exist
409ConflictDuplicate resource, concurrent modification conflict
422Unprocessable EntityValidation errors (preferred over 400 for validation)
429Too Many RequestsRate limit exceeded; include Retry-After header
500Internal Server ErrorUnexpected server error; log it, never expose details
503Service UnavailableServer overloaded or down for maintenance

cURL API Testing — Essential Flags and Patterns

curl is the universal API testing tool available on every platform. Mastering its flags makes ad-hoc API exploration and CI debugging much faster.

# ─── Basic request flags ─────────────────────────────────────────────────────
curl https://api.example.com/users          # GET (default)
curl -X POST https://api.example.com/users  # -X method
curl -v https://api.example.com/users       # -v verbose (shows headers)
curl -I https://api.example.com/users       # -I HEAD only (headers)
curl -L https://example.com/redirect        # -L follow redirects

# ─── Headers and authentication ──────────────────────────────────────────────
curl -H "Authorization: Bearer TOKEN" https://api.example.com/me
curl -H "X-API-Key: mykey" -H "Accept: application/json" https://api.example.com/data
curl -u username:password https://api.example.com/basic-auth  # Basic auth
curl --oauth2-bearer TOKEN https://api.example.com/oauth     # OAuth2

# ─── Sending request body ────────────────────────────────────────────────────
# JSON body (modern curl 7.82+)
curl --json '{"name":"Alice","email":"alice@example.com"}' https://api.example.com/users

# JSON body (older curl)
curl -X POST   -H "Content-Type: application/json"   -d '{"name":"Alice","email":"alice@example.com"}'   https://api.example.com/users

# Form data (application/x-www-form-urlencoded)
curl -X POST -d "name=Alice&email=alice@example.com" https://api.example.com/users

# Multipart form (file upload)
curl -X POST -F "file=@photo.jpg" -F "userId=123" https://api.example.com/upload

# ─── Output and inspection ───────────────────────────────────────────────────
curl -s https://api.example.com/users | jq '.'           # pretty-print JSON
curl -s https://api.example.com/users | jq '.[0].email'  # extract field
curl -w "
Status: %{http_code}
Time: %{time_total}s
"   https://api.example.com/users -o /dev/null             # status + timing only

# ─── Save and replay ─────────────────────────────────────────────────────────
curl -c cookies.txt -b cookies.txt https://api.example.com/login  # cookie jar
curl -o response.json https://api.example.com/data                # save body

The -v flag is essential for debugging — it shows the full request and response including TLS handshake, request headers, response headers, and body. Pipe the output through grep -i "content-type\|status" to focus on specific headers.

JavaScript fetch and axios — Error Handling, Interceptors, and Retry

// ─── fetch with proper error handling ───────────────────────────────────────
// NOTE: fetch() only rejects on network failure — 4xx/5xx are NOT errors!
async function apiFetch<T>(url: string, options?: RequestInit): Promise<T> {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), 10_000);  // 10s timeout

  try {
    const response = await fetch(url, {
      ...options,
      signal: controller.signal,
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${getToken()}`,
        ...options?.headers,
      },
    });

    clearTimeout(timeoutId);

    if (!response.ok) {
      const errorBody = await response.json().catch(() => ({}));
      throw new ApiError(response.status, response.statusText, errorBody);
    }

    // 204 No Content has no body
    if (response.status === 204) return undefined as T;
    return response.json() as Promise<T>;
  } catch (err) {
    if (err instanceof DOMException && err.name === 'AbortError') {
      throw new Error('Request timed out after 10 seconds');
    }
    throw err;
  }
}

class ApiError extends Error {
  constructor(
    public status: number,
    public statusText: string,
    public body: unknown
  ) {
    super(`HTTP ${status}: ${statusText}`);
  }
}

// Usage
const user = await apiFetch<User>('https://api.example.com/me');

Axios with Interceptors and Automatic Retry

import axios, { AxiosInstance, AxiosError } from 'axios';

// Create a configured axios instance
const api: AxiosInstance = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 10_000,
  headers: { 'Content-Type': 'application/json' },
});

// ─── Request interceptor: attach auth token ───────────────────────────────────
api.interceptors.request.use((config) => {
  const token = localStorage.getItem('accessToken');
  if (token) config.headers.Authorization = `Bearer ${token}`;
  return config;
});

// ─── Response interceptor: handle 401 refresh + retry ────────────────────────
let isRefreshing = false;
let failedQueue: Array<{ resolve: (v: unknown) => void; reject: (e: unknown) => void }> = [];

api.interceptors.response.use(
  (response) => response,
  async (error: AxiosError) => {
    const originalRequest = error.config as typeof error.config & { _retry?: boolean };

    if (error.response?.status === 401 && !originalRequest._retry) {
      if (isRefreshing) {
        return new Promise((resolve, reject) => {
          failedQueue.push({ resolve, reject });
        }).then(() => api(originalRequest));
      }

      originalRequest._retry = true;
      isRefreshing = true;

      try {
        const { data } = await axios.post('/auth/refresh');
        localStorage.setItem('accessToken', data.accessToken);
        failedQueue.forEach(({ resolve }) => resolve(null));
        failedQueue = [];
        return api(originalRequest);
      } catch (refreshError) {
        failedQueue.forEach(({ reject }) => reject(refreshError));
        failedQueue = [];
        window.location.href = '/login';
        return Promise.reject(refreshError);
      } finally {
        isRefreshing = false;
      }
    }

    return Promise.reject(error);
  }
);

export default api;

Postman and Insomnia — Collections, Tests, and Newman CLI

Postman and Insomnia are GUI API clients that add collection organization, environment variables, pre-request scripts, and test assertions on top of raw HTTP requests.

Postman Test Scripts (JavaScript)

// Postman "Tests" tab — runs after each response
// Access: pm.response, pm.environment, pm.globals, pm.collectionVariables

// ─── Status code assertions ───────────────────────────────────────────────────
pm.test("Status code is 200", () => {
  pm.response.to.have.status(200);
});

pm.test("Response time is under 500ms", () => {
  pm.expect(pm.response.responseTime).to.be.below(500);
});

// ─── Response body assertions ──────────────────────────────────────────────────
pm.test("Response has required fields", () => {
  const json = pm.response.json();
  pm.expect(json).to.have.property('id');
  pm.expect(json.email).to.be.a('string').and.include('@');
  pm.expect(json.role).to.be.oneOf(['admin', 'user', 'editor']);
});

// ─── Extract and save to environment ─────────────────────────────────────────
pm.test("Save access token", () => {
  const json = pm.response.json();
  pm.environment.set("accessToken", json.accessToken);
  pm.environment.set("userId", json.user.id);
});

// ─── Pre-request script (runs before sending) ─────────────────────────────────
// Add HMAC signature to request
const timestamp = Date.now().toString();
const signature = CryptoJS.HmacSHA256(
  timestamp + pm.environment.get("apiSecret"),
  pm.environment.get("apiKey")
).toString();
pm.request.headers.add({ key: "X-Timestamp", value: timestamp });
pm.request.headers.add({ key: "X-Signature", value: signature });

Newman CLI — Run Postman Collections in CI

npm install -g newman newman-reporter-htmlextra

# Run a collection with environment file
newman run MyAPI.postman_collection.json   -e production.postman_environment.json   --reporters cli,htmlextra   --reporter-htmlextra-export reports/api-test-report.html

# Run with iteration data (data-driven testing)
newman run MyAPI.postman_collection.json   --iteration-data test-data.json   --iteration-count 5

# GitHub Actions integration
# .github/workflows/api-tests.yml
jobs:
  api-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm install -g newman
      - run: newman run collection.json -e env.json --bail

Node.js supertest — Integration Testing Express APIs

supertest fires real HTTP requests against your Express app without starting a server, making it the standard choice for Node.js API integration tests.

npm install --save-dev supertest @types/supertest vitest
// tests/users.test.ts
import request from 'supertest';
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import app from '../src/app';           // your Express app (not .listen())
import { db } from '../src/db';

beforeAll(async () => {
  await db.migrate.latest();
  await db.seed.run();                 // seed test data
});

afterAll(async () => {
  await db.destroy();
});

describe('GET /users', () => {
  it('returns 200 with user list', async () => {
    const res = await request(app)
      .get('/users')
      .set('Authorization', 'Bearer ' + TEST_TOKEN)
      .expect(200)
      .expect('Content-Type', /json/);

    expect(res.body).toBeInstanceOf(Array);
    expect(res.body.length).toBeGreaterThan(0);
    expect(res.body[0]).toMatchObject({
      id: expect.any(String),
      email: expect.stringContaining('@'),
    });
  });

  it('returns 401 without token', async () => {
    await request(app).get('/users').expect(401);
  });
});

describe('POST /users', () => {
  it('creates a new user and returns 201', async () => {
    const newUser = { name: 'Bob', email: 'bob@example.com', role: 'user' };

    const res = await request(app)
      .post('/users')
      .set('Authorization', 'Bearer ' + ADMIN_TOKEN)
      .send(newUser)
      .expect(201);

    expect(res.body.id).toBeDefined();
    expect(res.body.email).toBe(newUser.email);
    expect(res.headers.location).toMatch(//users/.+/);
  });

  it('returns 422 for invalid email', async () => {
    const res = await request(app)
      .post('/users')
      .set('Authorization', 'Bearer ' + ADMIN_TOKEN)
      .send({ name: 'Bad', email: 'not-an-email', role: 'user' })
      .expect(422);

    expect(res.body.errors).toContainEqual(
      expect.objectContaining({ field: 'email' })
    );
  });

  it('returns 409 for duplicate email', async () => {
    await request(app)
      .post('/users')
      .set('Authorization', 'Bearer ' + ADMIN_TOKEN)
      .send({ name: 'Alice', email: 'alice@example.com', role: 'user' })
      .expect(409);
  });
});

Python httpx and pytest — Async API Testing

pip install httpx pytest pytest-asyncio respx
# tests/test_api.py
import pytest
import httpx
from httpx import AsyncClient

BASE_URL = "https://api.example.com"

# ─── Synchronous client ────────────────────────────────────────────────────────
def test_get_users():
    with httpx.Client(base_url=BASE_URL, timeout=10.0) as client:
        response = client.get("/users", headers={"Authorization": f"Bearer {TOKEN}"})
    assert response.status_code == 200
    data = response.json()
    assert isinstance(data, list)
    assert len(data) > 0

# ─── Async client ──────────────────────────────────────────────────────────────
@pytest.mark.asyncio
async def test_create_user():
    async with AsyncClient(base_url=BASE_URL) as client:
        response = await client.post(
            "/users",
            json={"name": "Alice", "email": "alice@example.com"},
            headers={"Authorization": f"Bearer {ADMIN_TOKEN}"},
        )
    assert response.status_code == 201
    data = response.json()
    assert "id" in data
    assert data["email"] == "alice@example.com"

# ─── Fixtures for shared setup ────────────────────────────────────────────────
@pytest.fixture
def auth_client():
    with httpx.Client(
        base_url=BASE_URL,
        headers={"Authorization": f"Bearer {TOKEN}"},
        timeout=10.0,
    ) as client:
        yield client

def test_get_profile(auth_client):
    response = auth_client.get("/me")
    assert response.status_code == 200
    assert response.json()["email"] == "test@example.com"

# ─── Mocking with respx ───────────────────────────────────────────────────────
import respx
from httpx import Response

@respx.mock
def test_external_api_call():
    respx.get("https://external-api.com/data").mock(
        return_value=Response(200, json={"value": 42})
    )

    with httpx.Client() as client:
        response = client.get("https://external-api.com/data")

    assert response.json()["value"] == 42
    assert respx.calls.call_count == 1

Mock API Servers — json-server, MSW, Mockoon, WireMock

Mock servers let frontend developers work independently from the backend, enable testing without real external dependencies, and reproduce edge cases on demand.

json-server — Zero-Config REST Mock

npm install -g json-server

# db.json
{
  "users": [
    { "id": 1, "name": "Alice", "email": "alice@example.com" },
    { "id": 2, "name": "Bob",   "email": "bob@example.com"   }
  ],
  "posts": []
}

# Start on port 3001
json-server --watch db.json --port 3001

# Automatically supports:
# GET    /users          → list all
# GET    /users/1        → get by id
# POST   /users          → create (auto-assigns id)
# PUT    /users/1        → replace
# PATCH  /users/1        → partial update
# DELETE /users/1        → delete
# GET    /users?name=Alice       → filter
# GET    /users?_sort=name&_order=asc  → sort
# GET    /users?_page=1&_limit=10      → paginate

MSW (Mock Service Worker) — Intercept at Network Level

npm install msw --save-dev
npx msw init public/  # generate service worker file

# src/mocks/handlers.ts
import { http, HttpResponse } from 'msw';

export const handlers = [
  http.get('https://api.example.com/users', () => {
    return HttpResponse.json([
      { id: '1', name: 'Alice', email: 'alice@example.com' },
    ]);
  }),

  http.post('https://api.example.com/users', async ({ request }) => {
    const body = await request.json() as { name: string; email: string };
    return HttpResponse.json(
      { id: crypto.randomUUID(), ...body },
      { status: 201 }
    );
  }),

  // Simulate error states
  http.get('https://api.example.com/error-endpoint', () => {
    return HttpResponse.json(
      { error: 'Internal server error' },
      { status: 500 }
    );
  }),
];

// src/mocks/browser.ts (for browser)
import { setupWorker } from 'msw/browser';
import { handlers } from './handlers';
export const worker = setupWorker(...handlers);

// src/mocks/server.ts (for Node.js/Vitest)
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
export const server = setupServer(...handlers);

// vitest.setup.ts
import { server } from './src/mocks/server';
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

API Contract Testing — Pact, OpenAPI Validation, and schemathesis

Contract tests verify that API consumers and providers agree on the API interface, enabling independent deployment without integration environment dependencies.

Pact Consumer-Driven Contract Testing (JavaScript)

npm install @pact-foundation/pact --save-dev

// consumer.pact.test.ts — run on the consumer (frontend) side
import { PactV3, MatchersV3 } from '@pact-foundation/pact';
import { fetchUser } from '../src/api/users';
import path from 'path';

const { like, string } = MatchersV3;

const provider = new PactV3({
  consumer: 'frontend-app',
  provider: 'user-api',
  dir: path.resolve(process.cwd(), 'pacts'),
});

describe('User API contract', () => {
  it('fetches a user by ID', () => {
    return provider.addInteraction({
      states: [{ description: 'user 123 exists' }],
      uponReceiving: 'a request for user 123',
      withRequest: {
        method: 'GET',
        path: '/users/123',
        headers: { Accept: 'application/json' },
      },
      willRespondWith: {
        status: 200,
        headers: { 'Content-Type': 'application/json' },
        body: {
          id: like('123'),
          name: like('Alice'),
          email: string(),
        },
      },
    }).executeTest(async (mockServer) => {
      const user = await fetchUser('123', mockServer.url);
      expect(user.id).toBeDefined();
      expect(user.email).toBeDefined();
    });
  });
});
// Pact writes a pact file to /pacts/frontend-app-user-api.json
// Provider side runs: pact-provider-verifier verifying against this file

OpenAPI Spec Validation with schemathesis

pip install schemathesis

# Automatically generate and run tests from OpenAPI spec
schemathesis run https://api.example.com/openapi.json   --checks all   --auth "Bearer TOKEN"   --base-url https://api.example.com   --hypothesis-max-examples 100

# schemathesis generates random valid (and invalid) inputs for every endpoint
# and checks for: 5xx responses, schema validation failures,
# response content-type mismatches, and more

# Run against local spec file
schemathesis run ./openapi.yaml --base-url http://localhost:3000

Load Testing — k6, Artillery, and Apache Bench

Load tests validate that your API meets performance SLAs under realistic and peak traffic conditions.

k6 Load Test Script

// k6-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate, Trend } from 'k6/metrics';

const errorRate = new Rate('error_rate');
const responseTime = new Trend('response_time');

export const options = {
  stages: [
    { duration: '30s', target: 10 },   // ramp up to 10 VUs
    { duration: '1m',  target: 50 },   // ramp up to 50 VUs
    { duration: '2m',  target: 50 },   // hold at 50 VUs
    { duration: '30s', target: 0 },    // ramp down
  ],
  thresholds: {
    http_req_duration: ['p(95)<500', 'p(99)<1000'],  // 95th %ile < 500ms
    http_req_failed: ['rate<0.01'],                   // error rate < 1%
    error_rate: ['rate<0.05'],
  },
};

const BASE_URL = 'https://api.example.com';
const TOKEN = __ENV.API_TOKEN;

export function setup() {
  // Runs once before load — e.g., create test user
  const res = http.post(`${BASE_URL}/auth/login`, JSON.stringify({
    email: 'loadtest@example.com',
    password: 'testpass',
  }), { headers: { 'Content-Type': 'application/json' } });
  return { token: res.json('accessToken') };
}

export default function (data) {
  const headers = {
    'Authorization': `Bearer ${data.token}`,
    'Content-Type': 'application/json',
  };

  // Test 1: List users
  const listRes = http.get(`${BASE_URL}/users`, { headers });
  check(listRes, {
    'list: status 200': (r) => r.status === 200,
    'list: has data': (r) => r.json('length') > 0,
  });
  errorRate.add(listRes.status !== 200);
  responseTime.add(listRes.timings.duration);

  sleep(1);  // think time between requests

  // Test 2: Create user
  const createRes = http.post(`${BASE_URL}/users`, JSON.stringify({
    name: 'Load Test User',
    email: `loadtest+${Date.now()}@example.com`,
  }), { headers });
  check(createRes, { 'create: status 201': (r) => r.status === 201 });

  sleep(0.5);
}

// Run: k6 run k6-test.js -e API_TOKEN=mytoken

Artillery YAML Config and Apache Bench

# artillery-config.yml
config:
  target: 'https://api.example.com'
  phases:
    - duration: 60
      arrivalRate: 10     # 10 new users per second for 60s
    - duration: 120
      arrivalRate: 50     # ramp to 50/s for 2 minutes
  defaults:
    headers:
      Authorization: 'Bearer {{ $processEnvironment.API_TOKEN }}'
      Content-Type: 'application/json'

scenarios:
  - name: "API smoke test"
    flow:
      - get:
          url: '/users'
          expect:
            - statusCode: 200
      - post:
          url: '/users'
          json:
            name: '{{ $randomString(8) }}'
            email: '{{ $randomString(8) }}@test.com'
          expect:
            - statusCode: 201

# Run: artillery run artillery-config.yml

# ─── Apache Bench (ab) — quick smoke test ────────────────────────────────────
# -n total requests, -c concurrent requests
ab -n 1000 -c 50 -H "Authorization: Bearer TOKEN"   https://api.example.com/users

# ─── wrk — modern high-performance benchmarking ──────────────────────────────
# -t threads, -c connections, -d duration
wrk -t4 -c100 -d30s https://api.example.com/users

API Documentation — OpenAPI 3.1, Swagger UI, and JSDoc

Good API documentation is testable documentation. An OpenAPI spec serves as both human-readable docs and machine-readable contract for validation, mocking, and SDK generation.

OpenAPI 3.1 Spec Structure

# openapi.yaml
openapi: '3.1.0'
info:
  title: My API
  version: '1.0.0'
  description: 'A sample API demonstrating OpenAPI 3.1 structure'
  contact:
    email: api@example.com

servers:
  - url: https://api.example.com
    description: Production
  - url: http://localhost:3000
    description: Local development

security:
  - bearerAuth: []

paths:
  /users:
    get:
      summary: List all users
      operationId: listUsers
      tags: [Users]
      parameters:
        - name: limit
          in: query
          schema: { type: integer, default: 20, maximum: 100 }
        - name: email
          in: query
          schema: { type: string, format: email }
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                type: array
                items: { $ref: '#/components/schemas/User' }
        '401':
          $ref: '#/components/responses/Unauthorized'

    post:
      summary: Create a user
      operationId: createUser
      tags: [Users]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/CreateUserInput' }
      responses:
        '201':
          description: User created
          headers:
            Location:
              schema: { type: string, example: '/users/123' }
          content:
            application/json:
              schema: { $ref: '#/components/schemas/User' }
        '422':
          $ref: '#/components/responses/ValidationError'

components:
  schemas:
    User:
      type: object
      required: [id, email, name, createdAt]
      properties:
        id:   { type: string, format: uuid }
        email: { type: string, format: email }
        name:  { type: string }
        role:  { type: string, enum: [admin, user, editor] }
        createdAt: { type: string, format: date-time }

    CreateUserInput:
      type: object
      required: [email, name]
      properties:
        email: { type: string, format: email }
        name:  { type: string, minLength: 2, maxLength: 100 }
        role:  { type: string, enum: [user, editor], default: user }

  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

  responses:
    Unauthorized:
      description: Authentication required
      content:
        application/json:
          schema:
            type: object
            properties:
              error: { type: string }
    ValidationError:
      description: Validation failed
      content:
        application/json:
          schema:
            type: object
            properties:
              errors:
                type: array
                items:
                  type: object
                  properties:
                    field: { type: string }
                    message: { type: string }

Swagger UI and Redoc Setup

# Express: serve Swagger UI from your API
npm install swagger-ui-express yaml

import swaggerUi from 'swagger-ui-express';
import { readFileSync } from 'fs';
import YAML from 'yaml';

const spec = YAML.parse(readFileSync('./openapi.yaml', 'utf8'));
app.use('/docs', swaggerUi.serve, swaggerUi.setup(spec));
// → http://localhost:3000/docs

# FastAPI auto-generates OpenAPI + Swagger UI at /docs
# (no configuration needed — decorators define the schema)

# Redoc (clean read-only documentation)
npx @redocly/cli preview-docs openapi.yaml
# Or serve statically:
npx @redocly/cli build-docs openapi.yaml --output docs/index.html

Convert any cURL command to code — paste a curl command into our cURL to code converter to get equivalent JavaScript fetch, axios, Python httpx, Go, or Ruby code instantly.

Key Takeaways

  • GET is safe and idempotent; POST is neither. Always return 201 with a Location header after successful POST creation.
  • cURL -v shows everything: use it with grep -i to isolate specific headers during debugging. Pipe output to jq for JSON formatting.
  • fetch() does not throw on 4xx/5xx: always check response.ok or response.status explicitly.
  • Axios interceptors are the right place to handle token refresh and global error normalization — keep them out of individual API calls.
  • supertest for Node.js integration tests: test the full HTTP stack including middleware, routing, and response serialization without starting a real server.
  • MSW intercepts at the network level: works with any fetch/axios call transparently and is the best mock for React component tests.
  • Contract tests with Pact enable independent deployment — if the provider passes pact tests, it is safe to deploy without coordinating with consumers.
  • k6 thresholds fail the test automatically: set p(95)<500 and rate<0.01 to catch regressions in CI.
  • OpenAPI 3.1 is both documentation and contract: generate mocks, validate responses, and create SDKs from a single source of truth.
  • schemathesis auto-generates test cases from your OpenAPI spec — it finds edge cases (empty strings, null values, boundary numbers) that hand-written tests miss.
𝕏 Twitterin LinkedIn
War das hilfreich?

Bleiben Sie informiert

Wöchentliche Dev-Tipps und neue Tools.

Kein Spam. Jederzeit abbestellbar.

Verwandte Tools ausprobieren

>>cURL to Code Converter{ }JSON Formatter🔓CORS Tester

Verwandte Artikel

JWT-Authentifizierung: Vollstaendiger Implementierungsguide

JWT-Authentifizierung von Grund auf implementieren. Token-Struktur, Access- und Refresh-Tokens, Node.js-Implementierung, Client-seitige Verwaltung, Sicherheits-Best-Practices und Next.js-Middleware.

CORS-Tester: CORS-Fehler Beheben und Cross-Origin-Anfragen Konfigurieren — Komplette Anleitung

CORS-Fehler beheben und Cross-Origin-Anfragen konfigurieren. Anleitung zu CORS-Headern, Preflight-Anfragen, Express/Next.js/Nginx/FastAPI, Credentials und Sicherheit.

REST API Best Practices: Der komplette Leitfaden für 2026

REST API Design Best Practices: Namenskonventionen, Fehlerbehandlung, Authentifizierung, Paginierung und Sicherheit.