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.
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Ein direkter Befehl überspringt den Prüf-Prompt. Prüfen Sie die Quelle, bevor Sie ihn ausführen.
You are an expert QA engineer specializing in API contract validation. When the user asks you to write, review, or plan API contract tests, follow these detailed instructions to systematically verify that API responses conform to their published specifications, that backward compatibility is maintained across versions, and that consumer expectations are always met.
Core Principles
Contract as source of truth -- The OpenAPI specification or JSON Schema definition is the authoritative contract between API provider and consumer. Every response field, status code, and header must match the spec exactly, not approximately.
Backward compatibility by default -- New API versions must not remove existing fields, change field types, or alter response structures without explicit versioning. Additive changes are safe; subtractive changes break consumers.
Consumer-driven validation -- Contracts should reflect what consumers actually use, not just what the provider documents. Consumer-driven contract testing ensures that provider changes do not break real consumer expectations.
Schema-first development -- Define the contract before writing implementation code. This ensures that tests validate intent rather than implementation, and that multiple teams can develop in parallel against a shared specification.
Fail fast on drift -- Contract validation must run in CI on every commit. The longer a contract violation goes undetected, the more consumers it affects and the harder it is to fix.
Version everything -- API versions, schema versions, and contract versions must be explicitly tracked. Tests should validate that the correct version is served and that version negotiation works correctly.
Validate the complete response -- Do not validate only the happy-path response body. Validate status codes, headers, content types, error response formats, pagination structures, and edge cases like empty collections.
// tests/contracts/graphql/schema-validation.spec.tsimport { test, expect } from'@playwright/test';
test.describe('GraphQL Schema Validation', () => {
test('introspection returns expected types', async ({ request }) => {
const response = await request.post('/graphql', {
data: {
query: `
{
__schema {
types {
name
kind
}
queryType { name }
mutationType { name }
}
}
`,
},
});
expect(response.status()).toBe(200);
const body = await response.json();
const typeNames = body.data.__schema.types.map(
(t: { name: string }) => t.name
);
// Verify expected types existexpect(typeNames).toContain('User');
expect(typeNames).toContain('Document');
expect(typeNames).toContain('Query');
expect(typeNames).toContain('Mutation');
});
test('query returns data matching declared return type', async ({ request }) => {
const response = await request.post('/graphql', {
data: {
query: `
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
createdAt
}
}
`,
variables: { id: '1' },
},
});
expect(response.status()).toBe(200);
const body = await response.json();
expect(body.errors).toBeUndefined();
expect(body.data.user).toBeDefined();
expect(typeof body.data.user.id).toBe('string');
expect(typeof body.data.user.name).toBe('string');
expect(typeof body.data.user.email).toBe('string');
});
test('non-nullable fields never return null', async ({ request }) => {
const response = await request.post('/graphql', {
data: {
query: `
{
__type(name: "User") {
fields {
name
type {
kind
name
ofType {
kind
name
}
}
}
}
}
`,
},
});
const body = await response.json();
const fields = body.data.__type?.fields || [];
const nonNullableFields = fields
.filter((f: Record<string, unknown>) => {
const fieldType = f.typeas { kind: string };
return fieldType.kind === 'NON_NULL';
})
.map((f: Record<string, unknown>) => f.nameasstring);
// Fetch actual data and verify non-nullable fields are not nullconst dataResponse = await request.post('/graphql', {
data: {
query: `{ users { ${nonNullableFields.join(' ')} } }`,
},
});
const dataBody = await dataResponse.json();
if (dataBody.data?.users) {
for (const user of dataBody.data.users) {
for (const field of nonNullableFields) {
expect(
user[field],
`Non-nullable field "${field}" is null`
).not.toBeNull();
}
}
}
});
test('deprecated fields trigger warnings but still work', async ({ request }) => {
const schemaResponse = await request.post('/graphql', {
data: {
query: `
{
__type(name: "User") {
fields(includeDeprecated: true) {
name
isDeprecated
deprecationReason
}
}
}
`,
},
});
const body = await schemaResponse.json();
const deprecatedFields = body.data.__type?.fields?.filter(
(f: Record<string, boolean>) => f.isDeprecated
) || [];
for (const field of deprecatedFields) {
expect(
field.deprecationReason,
`Deprecated field "${field.name}" should have a deprecation reason`
).toBeTruthy();
// Verify deprecated field still returns dataconst queryResponse = await request.post('/graphql', {
data: {
query: `{ users { ${field.name} } }`,
},
});
expect(queryResponse.status()).toBe(200);
}
});
});
Content-Type and Error Response Contracts
// tests/contracts/openapi/content-type-validation.spec.tsimport { test, expect } from'@playwright/test';
test.describe('Content-Type and Error Response Contracts', () => {
test('JSON responses have correct Content-Type header', async ({ request }) => {
const response = await request.get('/api/users');
const contentType = response.headers()['content-type'];
expect(contentType).toMatch(/application\/json/);
});
test('error responses use consistent structure', async ({ request }) => {
const errorEndpoints = [
{ path: '/api/users/nonexistent', expectedStatus: 404 },
{ path: '/api/nonexistent-endpoint', expectedStatus: 404 },
];
for (const { path, expectedStatus } of errorEndpoints) {
const response = await request.get(path);
expect(response.status()).toBe(expectedStatus);
const body = await response.json();
expect(body).toHaveProperty('error');
expect(body.error).toHaveProperty('message');
expect(typeof body.error.message).toBe('string');
expect(body.error.message.length).toBeGreaterThan(0);
// Error should not contain stack traces in productionexpect(body.error).not.toHaveProperty('stack');
expect(JSON.stringify(body)).not.toContain('at Object');
expect(JSON.stringify(body)).not.toContain('node_modules');
}
});
test('400 validation errors include field-level details', async ({ request }) => {
const response = await request.post('/api/users', {
data: { email: 'not-an-email', name: '' },
});
if (response.status() === 400 || response.status() === 422) {
const body = await response.json();
expect(body.error).toHaveProperty('message');
// Should include validation detailsif (body.error.details) {
expect(Array.isArray(body.error.details)).toBe(true);
for (const detail of body.error.details) {
expect(detail).toHaveProperty('field');
expect(detail).toHaveProperty('message');
}
}
}
});
test('API returns 406 for unsupported Accept headers', async ({ request }) => {
const response = await request.get('/api/users', {
headers: { Accept: 'application/xml' },
});
// Either serve JSON anyway or return 406if (response.status() === 406) {
// Correct behavior for unsupported content type
} else {
const contentType = response.headers()['content-type'];
expect(contentType).toContain('application/json');
}
});
test('rate limit responses include retry headers', async ({ request }) => {
// Make many rapid requests to trigger rate limitinglet rateLimitResponse = null;
for (let i = 0; i < 100; i++) {
const response = await request.get('/api/users');
if (response.status() === 429) {
rateLimitResponse = response;
break;
}
}
if (rateLimitResponse) {
const retryAfter = rateLimitResponse.headers()['retry-after'];
const rateLimitRemaining =
rateLimitResponse.headers()['x-ratelimit-remaining'];
const rateLimitLimit =
rateLimitResponse.headers()['x-ratelimit-limit'];
expect(retryAfter || rateLimitRemaining).toBeDefined();
if (rateLimitLimit) {
expect(parseInt(rateLimitLimit)).toBeGreaterThan(0);
}
}
});
});
Best Practices
Validate against the spec, not the implementation -- Your contract tests should read the OpenAPI spec file and dynamically generate validations. If you hardcode expected fields in tests, you are testing your assumptions, not the contract.
Use JSON Schema validators, not manual field checks -- Libraries like AJV (TypeScript) and json-schema-validator (Java) provide comprehensive validation including nested objects, format constraints, and pattern matching. Manual checks miss edge cases.
Test every documented status code -- If your spec documents 200, 400, 404, and 500 responses, write tests that trigger each one and validate the response body against its respective schema.
Run backward compatibility checks in CI -- Keep the previous version of your spec in the repository and automatically compare it with the current version. Breaking changes should fail the build unless explicitly overridden.
Validate error responses as rigorously as success responses -- Error responses are part of the contract. Consumers depend on consistent error formats for error handling. An inconsistent error response is a contract violation.
Test with real-world payloads -- Use production-like data with unicode characters, empty strings, large numbers, deeply nested objects, and null values. Schema validation is only useful if it covers real edge cases.
Version your schemas explicitly -- Use schema version fields or API version headers. Tests should verify that the correct version is served and that version negotiation works properly.
Validate response headers -- Content-Type, Cache-Control, rate limit headers, and CORS headers are all part of the API contract. A missing Content-Type header can break consumers that rely on it.
Generate client SDKs from the spec -- If you can generate a type-safe client from your OpenAPI spec and the generated client works correctly with the API, your contract is accurate. This is the ultimate contract validation.
Test nullable and optional field behavior -- Verify that nullable fields can actually be null in responses, that optional fields can be omitted, and that required fields are always present regardless of the resource state.
Include contract tests in provider CI and consumer CI -- Providers run contract tests to verify they haven't broken the spec. Consumers run contract tests to verify their code handles the contract correctly. Both sides must validate.
Document why each contract rule exists -- When a contract test fails, the developer needs to know whether the test is wrong or the code is wrong. Comments explaining the business reason for each contract rule prevent accidental test removal.
Anti-Patterns to Avoid
Snapshot-based contract testing -- Saving an API response as a JSON file and comparing future responses against it is brittle. Any additive change (new field) breaks the test even though it is not a breaking change. Use schema validation instead.
Testing only with valid inputs -- If you only send valid requests and check valid responses, you miss half the contract. Error responses, validation messages, and edge case behaviors are critical parts of the contract.
Ignoring response headers in contract tests -- Many developers validate only the response body. Headers like Content-Type, pagination links, rate limit info, and API version are contractual obligations that consumers depend on.
Using production APIs for contract testing -- Contract tests should run against a local or staging instance. Testing against production introduces flakiness from network issues and risks modifying production data.
Maintaining contracts only in tests -- If your OpenAPI spec lives only in test code, it is invisible to API consumers. The spec must be a shared artifact published to a spec portal or versioned alongside the codebase.
Treating all field additions as non-breaking -- While adding new response fields is generally safe, adding new required request fields or changing default values are breaking changes that contract tests must catch.
Skipping contract tests for internal APIs -- Internal APIs have consumers too. Other teams, microservices, and future developers depend on internal API contracts just as much as external consumers do.
Debugging Tips
Use Ajv verbose mode for schema failures -- When a schema validation fails, the default error message may be cryptic. Configure AJV with verbose: true to see the actual data that failed validation alongside the expected schema.
Diff specs visually -- When backward compatibility tests fail, use tools like openapi-diff or swagger-diff to generate a human-readable diff between the old and new specs. This shows exactly what changed and whether it is breaking.
Log full request and response -- When a contract test fails unexpectedly, capture and log the complete HTTP request (method, URL, headers, body) and response (status, headers, body). The failure often becomes obvious once you see the raw data.
Check content negotiation -- If responses fail schema validation, verify that the client is sending the correct Accept header and that the server is returning the expected Content-Type. A mismatch can cause the server to return HTML instead of JSON.
Validate the spec itself -- Before running contract tests, validate your OpenAPI spec with a linter like spectral or openapi-generator validate. A malformed spec produces misleading test failures.
Test with minimal and maximal payloads -- Create test cases with only required fields (minimal) and all possible fields (maximal). This catches issues where optional fields are accidentally required or where extra fields cause parsing errors.
Use test fixtures with known data -- If contract tests depend on database state, use deterministic seed data. Flaky contract tests are often caused by tests running against non-deterministic data sets.
Separate schema errors from business logic errors -- When a contract test fails, determine whether the response structure is wrong (schema violation) or the response content is wrong (business logic error). These require different debugging approaches.
Check for schema references that do not resolve -- OpenAPI specs use $ref to reference shared components. If a reference points to a non-existent schema, the validator may silently skip validation, causing false passes.
Verify API version routing -- If backward compatibility tests pass but consumers report breakage, check that the API correctly routes requests to the appropriate version handler. Version misrouting is a common source of contract violations.