Claude-skills api-contract-testing
Verifies API contracts between services using consumer-driven contracts, schema validation, and tools like Pact. Use when testing microservices communication, preventing breaking changes, or validating OpenAPI specifications.
install
source · Clone the upstream repo
git clone https://github.com/secondsky/claude-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/secondsky/claude-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/api-contract-testing/skills/api-contract-testing" ~/.claude/skills/secondsky-claude-skills-api-contract-testing && rm -rf "$T"
manifest:
plugins/api-contract-testing/skills/api-contract-testing/SKILL.mdsource content
API Contract Testing
Verify that APIs honor their contracts between consumers and providers without requiring full integration tests.
Key Concepts
| Term | Definition |
|---|---|
| Consumer | Service that calls an API |
| Provider | Service that exposes an API |
| Contract | Agreed request/response format |
| Pact | Consumer-driven contract testing tool |
| Schema | Structure definition (OpenAPI, JSON Schema) |
| Broker | Central repository for contracts |
Pact Consumer Test (TypeScript)
import { PactV3, MatchersV3 } from '@pact-foundation/pact'; const provider = new PactV3({ consumer: 'OrderService', provider: 'UserService' }); describe('User API Contract', () => { it('returns user by ID', async () => { await provider .given('user 123 exists') .uponReceiving('a request for user 123') .withRequest({ method: 'GET', path: '/users/123' }) .willRespondWith({ status: 200, body: MatchersV3.like({ id: '123', name: MatchersV3.string('John'), email: MatchersV3.email('john@example.com') }) }) .executeTest(async (mockServer) => { const response = await fetch(`${mockServer.url}/users/123`); expect(response.status).toBe(200); }); }); it('returns 404 for non-existent user', async () => { await provider .given('user does not exist') .uponReceiving('a request for non-existent user') .withRequest({ method: 'GET', path: '/users/999' }) .willRespondWith({ status: 404, body: MatchersV3.like({ error: { code: 'NOT_FOUND', message: MatchersV3.string() } }) }) .executeTest(async (mockServer) => { const response = await fetch(`${mockServer.url}/users/999`); expect(response.status).toBe(404); }); }); });
Provider Verification
import { Verifier } from '@pact-foundation/pact'; new Verifier({ provider: 'UserService', providerBaseUrl: 'http://localhost:3000', pactBrokerUrl: process.env.PACT_BROKER_URL, publishVerificationResult: true, providerVersion: process.env.GIT_SHA, stateHandlers: { 'user 123 exists': async () => { await db.users.create({ id: '123', name: 'John' }); }, 'user does not exist': async () => { await db.users.deleteAll(); } } }).verifyProvider();
OpenAPI Validation (Express)
const OpenApiValidator = require('express-openapi-validator'); app.use(OpenApiValidator.middleware({ apiSpec: './openapi.yaml', validateRequests: true, validateResponses: true, validateSecurity: true }));
Additional Implementations
- Python JSON Schema: See references/python-json-schema.md
- Java REST Assured: See references/java-rest-assured.md
- Pact Broker CI/CD: See references/pact-broker-cicd.md
Best Practices
Do:
- Test from consumer perspective
- Use matchers for flexible matching
- Validate structure, not specific values
- Version contracts explicitly
- Test error responses
- Run tests in CI pipeline
- Test backward compatibility
Don't:
- Test business logic in contracts
- Hard-code specific values
- Skip error scenarios
- Ignore versioning
- Deploy without verification
Tools
- Pact - Multi-language consumer-driven contracts
- Spring Cloud Contract - JVM ecosystem
- OpenAPI/Swagger - Schema-first validation
- Dredd - API blueprint testing
- Spectral - OpenAPI linting