Skip to main content Skills Marketplace Découvrez et explorez les compétences IA créées par la communauté.
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Copier le promptAfficher les détails du prompt Une commande directe contourne le prompt de vérification. Examinez la source avant de l'exécuter.
npx skills add https://github.com/ForceInjection/domain-driven-design-skills --skill api-contract-testingLa commande reste sur une seule ligne. Faites défiler horizontalement pour la vérifier avant de la copier.
Vous préférez une copie locale ? Téléchargez les fichiers actuellement disponibles dans SkillsMP.
Télécharger Zip Téléchargement... Conduct deep academic research for philosophy, neuroscience, cognitive science, and theoretical computer science (computability, complexity, AI theory, logic). Use when user asks to: research academic topics, find scholarly papers, conduct literature reviews, analyze citations, synthesize research findings, explore philosophical arguments, investigate consciousness/cognition, study computability/decidability/Turing machines, or analyze academic debates. Triggers on: 'research papers', 'literature review', 'academic sources', 'scholarly articles', 'philosophy of mind', 'computability theory', 'neuroscience studies', 'find papers on', 'what does the research say'.
Métiers associés SOC
Basé sur la classification professionnelle SOC
Explorateur de fichiers
2 fichiers name api-contract-testing description Verify API contracts between services to ensure compatibility and prevent breaking changes. Use for contract testing, Pact, API contract validation, schema validation, and consumer-driven contracts.
API Contract Testing
Overview
Contract testing verifies that APIs honor their contracts between consumers and providers. It ensures that service changes don't break dependent consumers without requiring full integration tests. Contract tests validate request/response formats, data types, and API behavior independently.
When to Use
Testing microservices communication
Preventing breaking API changes
Validating API versioning
Testing consumer-provider contracts
Ensuring backward compatibility
Validating OpenAPI/Swagger specifications
Testing third-party API integrations
Catching contract violations in CI
Key Concepts
Consumer : Service that calls an API
Provider : Service that exposes the API
Contract : Agreement on API request/response format
Pact : Consumer-defined expectations
Schema : Structure definition (OpenAPI, JSON Schema)
Stub : Generated mock from contract
Broker : Central repository for contracts
Instructions
1. Pact for Consumer-Driven Contracts
Consumer Test (Jest/Pact)
import { PactV3 , MatchersV3 } from ;
{ } ;
{ like, eachLike, iso8601DateTimeWithMillis } = ;
provider = ({
: ,
: ,
: ,
: ,
});
( , {
userService = ( );
( , {
( , () => {
provider
. ( )
. ( )
. ({
: ,
: ,
: {
: ( ),
},
})
. ({
: ,
: {
: ,
},
: {
: ( ),
: ( ),
: ( ),
: ( ),
: ( ),
: ( ),
},
})
. ( (mockServer) => {
user = userService. ( );
(user. ). ( );
(user. ). ();
(user. ). ();
});
});
( , () => {
provider
. ( )
. ( )
. ({
: ,
: ,
})
. ({
: ,
: {
: ,
},
: {
: ( ),
: ( ),
},
})
. ( (mockServer) => {
(userService. ( )). . (
);
});
});
});
( , {
( , () => {
provider
. ( )
. ( )
. ({
: ,
: ,
: {
: ,
},
: {
: ( ),
: ( ),
: ( ),
},
})
. ({
: ,
: {
: ,
},
: {
: ( ),
: ( ),
: ( ),
: ( ),
: (),
: ,
},
})
. ( (mockServer) => {
user = userService. ({
: ,
: ,
: ,
});
(user. ). ();
(user. ). ( );
});
});
});
( , {
( , () => {
provider
. ( )
. ( )
. ({
: ,
: ,
: {
: ,
: ,
},
})
. ({
: ,
: {
: ({
: ( ),
: ( ),
: ( ),
: (),
}),
: ( ),
: ( ),
},
})
. ( (mockServer) => {
response = userService. ( , {
: ,
: ,
});
(response. ). ();
( . (response. )). ( );
(response. ). ();
});
});
});
});
'@pact-foundation/pact'
import
UserService
from
'../../src/services/UserService'
const
MatchersV3
const
new
PactV3
consumer
'OrderService'
provider
'UserService'
port
1234
dir
'./pacts'
describe
'User Service Contract'
() =>
const
new
UserService
'http://localhost:1234'
describe
'GET /users/:id'
() =>
test
'returns user when found'
async
await
given
'user with ID 123 exists'
uponReceiving
'a request for user 123'
withRequest
method
'GET'
path
'/users/123'
headers
Authorization
like
'Bearer token'
willRespondWith
status
200
headers
'Content-Type'
'application/json'
body
id
like
'123'
email
like
'user@example.com'
name
like
'John Doe'
age
like
30
createdAt
iso8601DateTimeWithMillis
'2024-01-01T00:00:00.000Z'
role
like
'user'
executeTest
async
const
await
getUser
'123'
expect
id
toBe
'123'
expect
email
toBeDefined
expect
name
toBeDefined
test
'returns 404 when user not found'
async
await
given
'user with ID 999 does not exist'
uponReceiving
'a request for non-existent user'
withRequest
method
'GET'
path
'/users/999'
willRespondWith
status
404
headers
'Content-Type'
'application/json'
body
error
like
'User not found'
code
like
'USER_NOT_FOUND'
executeTest
async
await
expect
getUser
'999'
rejects
toThrow
'User not found'
describe
'POST /users'
() =>
test
'creates new user'
async
await
given
'user does not exist'
uponReceiving
'a request to create user'
withRequest
method
'POST'
path
'/users'
headers
'Content-Type'
'application/json'
body
email
like
'newuser@example.com'
name
like
'New User'
age
like
25
willRespondWith
status
201
headers
'Content-Type'
'application/json'
body
id
like
'new-123'
email
like
'newuser@example.com'
name
like
'New User'
age
like
25
createdAt
iso8601DateTimeWithMillis
role
'user'
executeTest
async
const
await
createUser
email
'newuser@example.com'
name
'New User'
age
25
expect
id
toBeDefined
expect
email
toBe
'newuser@example.com'
describe
'GET /users/:id/orders'
() =>
test
'returns user orders'
async
await
given
'user 123 has orders'
uponReceiving
'a request for user orders'
withRequest
method
'GET'
path
'/users/123/orders'
query
limit
'10'
offset
'0'
willRespondWith
status
200
body
orders
eachLike
id
like
'order-1'
total
like
99.99
status
like
'completed'
createdAt
iso8601DateTimeWithMillis
total
like
5
hasMore
like
false
executeTest
async
const
await
getUserOrders
'123'
limit
10
offset
0
expect
orders
toBeDefined
expect
Array
isArray
orders
toBe
true
expect
total
toBeDefined
Provider Test (Verify Contract)
import { Verifier } from '@pact-foundation/pact' ;
import path from 'path' ;
import { app } from '../../src/app' ;
import { setupTestDB, teardownTestDB } from '../helpers/db' ;
describe ('Pact Provider Verification' , () => {
let server;
beforeAll (async () => {
await setupTestDB ();
server = app.listen (3001 );
});
afterAll (async () => {
await teardownTestDB ();
server.close ();
});
test ('validates the expectations of OrderService' , () => {
return new Verifier ({
provider : 'UserService' ,
providerBaseUrl : 'http://localhost:3001' ,
pactUrls : [
path.resolve (__dirname, '../../pacts/orderservice-userservice.json' ),
],
stateHandlers : {
'user with ID 123 exists' : async () => {
await createTestUser ({ id : '123' , name : 'John Doe' });
},
'user with ID 999 does not exist' : async () => {
await deleteUser ('999' );
},
'user 123 has orders' : async () => {
await createTestUser ({ id : '123' });
await createTestOrder ({ userId : '123' });
},
},
})
.verifyProvider ()
.then ((output ) => {
console .log ('Pact Verification Complete!' );
});
});
});
2. OpenAPI Schema Validation
import request from 'supertest' ;
import { app } from '../../src/app' ;
import OpenAPIValidator from 'express-openapi-validator' ;
import fs from 'fs' ;
import yaml from 'js-yaml' ;
describe ('OpenAPI Contract Validation' , () => {
let validator;
beforeAll (() => {
const spec = yaml.load (
fs.readFileSync ('./openapi.yaml' , 'utf8' )
);
validator = OpenAPIValidator .middleware ({
apiSpec : spec,
validateRequests : true ,
validateResponses : true ,
});
});
test ('GET /users/:id matches schema' , async () => {
const response = await request (app)
.get ('/users/123' )
.expect (200 );
expect (response.body ).toMatchObject ({
id : expect.any (String ),
email : expect.stringMatching (/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/ ),
name : expect.any (String ),
age : expect.any (Number ),
createdAt : expect.stringMatching (/^\d{4}-\d{2}-\d{2}T/ ),
});
});
test ('POST /users validates request body' , async () => {
const invalidUser = {
email : 'invalid-email' ,
name : 'Test' ,
};
await request (app)
.post ('/users' )
.send (invalidUser)
.expect (400 );
});
});
3. JSON Schema Validation
import pytest
import jsonschema
from jsonschema import validate
import json
USER_SCHEMA = {
"type" : "object" ,
"required" : ["id" , "email" , "name" ],
"properties" : {
"id" : {"type" : "string" },
"email" : {"type" : "string" , "format" : "email" },
"name" : {"type" : "string" },
"age" : {"type" : "integer" , "minimum" : 0 , "maximum" : 150 },
"role" : {"type" : "string" , "enum" : ["user" , "admin" ]},
"createdAt" : {"type" : "string" , "format" : "date-time" },
},
"additionalProperties" : False
}
ORDER_SCHEMA = {
"type" : "object" ,
"required" : ["id" , "userId" , "total" , "status" ],
"properties" : {
"id" : {"type" : "string" },
"userId" : {"type" : "string" },
"total" : {"type" : "number" , "minimum" : 0 },
"status" : {
"type" : "string" ,
"enum" : ["pending" , "paid" , "shipped" , "delivered" , "cancelled" ]
},
"items" : {
"type" : "array" ,
"items" : {
"type" : "object" ,
"required" : ["productId" , "quantity" , "price" ],
"properties" : {
"productId" : {"type" : "string" },
"quantity" : {"type" : "integer" , "minimum" : 1 },
"price" : {"type" : "number" , "minimum" : 0 },
}
}
}
}
}
class TestAPIContracts :
def test_get_user_response_schema (self, api_client ):
"""Validate user endpoint response against schema."""
response = api_client.get('/api/users/123' )
assert response.status_code == 200
data = response.json()
validate(instance=data, schema=USER_SCHEMA)
def test_create_user_request_schema (self, api_client ):
"""Validate create user request body."""
valid_user = {
"email" : "test@example.com" ,
"name" : "Test User" ,
"age" : 30 ,
}
response = api_client.post('/api/users' , json=valid_user)
assert response.status_code == 201
validate(instance=response.json(), schema=USER_SCHEMA)
def test_invalid_request_rejected (self, api_client ):
"""Invalid requests should be rejected."""
invalid_user = {
"email" : "not-an-email" ,
"age" : -5 ,
}
response = api_client.post('/api/users' , json=invalid_user)
assert response.status_code == 400
def test_order_response_schema (self, api_client ):
"""Validate order endpoint response."""
response = api_client.get('/api/orders/order-123' )
assert response.status_code == 200
validate(instance=response.json(), schema=ORDER_SCHEMA)
def test_order_items_array_validation (self, api_client ):
"""Validate nested array schema."""
order_data = {
"userId" : "user-123" ,
"items" : [
{"productId" : "prod-1" , "quantity" : 2 , "price" : 29.99 },
{"productId" : "prod-2" , "quantity" : 1 , "price" : 49.99 },
]
}
response = api_client.post('/api/orders' , json=order_data)
assert response.status_code == 201
result = response.json()
validate(instance=result, schema=ORDER_SCHEMA)
4. REST Assured for Java
import io.restassured.RestAssured;
import io.restassured.module .jsv.JsonSchemaValidator;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;
public class UserAPIContractTest {
@Test
public void getUserShouldMatchSchema () {
given()
.pathParam("id" , "123" )
.when ()
.get("/api/users/{id}" )
.then()
.statusCode(200 )
.body(JsonSchemaValidator.matchesJsonSchemaInClasspath("schemas/user-schema.json" ))
.body("id" , notNullValue())
.body("email" , matchesPattern("^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$" ))
.body("age" , greaterThanOrEqualTo(0 ));
}
@Test
public void createUserShouldValidateRequest () {
String userJson = """
{
"email": "test@example.com",
"name": "Test User",
"age": 30
}
""" ;
given()
.contentType("application/json" )
.body(userJson)
.when ()
.post("/api/users" )
.then()
.statusCode(201 )
.body("id" , notNullValue())
.body("email" , equalTo("test@example.com" ))
.body("createdAt" , matchesPattern("\\d{4}-\\d{2}-\\d{2}T.*" ));
}
@Test
public void getUserOrdersShouldReturnArray () {
given()
.pathParam("id" , "123" )
.queryParam("limit" , 10 )
.when ()
.get("/api/users/{id}/orders" )
.then()
.statusCode(200 )
.body("orders" , isA(java.util.List.class))
.body("orders[0].id" , notNullValue())
.body("orders[0].status" , isIn(Arrays.asList(
"pending" , "paid" , "shipped" , "delivered" , "cancelled"
)))
.body("total" , greaterThanOrEqualTo(0 ));
}
@Test
public void invalidRequestShouldReturn400 () {
String invalidUser = """
{
"email": "not-an-email",
"age": -5
}
""" ;
given()
.contentType("application/json" )
.body(invalidUser)
.when ()
.post("/api/users" )
.then()
.statusCode(400 )
.body("error" , notNullValue());
}
}
5. Contract Testing with Postman
{
"info" : {
"name" : "User API Contract Tests"
} ,
"item" : [
{
"name" : "Get User" ,
"request" : {
"method" : "GET" ,
"url" : "{{baseUrl}}/users/{{userId}}"
} ,
"test" : "
pm.test('Response status is 200', () => {
pm.response.to.have.status(200);
});
pm.test('Response matches schema', () => {
const schema = {
type: 'object',
required: ['id', 'email', 'name'],
properties: {
id: { type: 'string' },
email: { type: 'string', format: 'email' },
name: { type: 'string' },
age: { type: 'integer' }
}
};
pm.response.to.have.jsonSchema(schema);
});
pm.test('Email format is valid', () => {
const data = pm.response.json();
pm.expect(data.email).to.match(/^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$/);
});
"
}
]
}
6. Pact Broker Integration
name: Contract Tests
on: [push , pull_request ]
jobs:
consumer-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm ci
- run: npm run test:pact
- name: Publish Pacts
run: |
npx pact-broker publish ./pacts \
--consumer-app-version=${{ github.sha }} \
--broker-base-url=${{ secrets.PACT_BROKER_URL }} \
--broker-token=${{ secrets.PACT_BROKER_TOKEN }}
provider-tests:
runs-on: ubuntu-latest
needs: consumer-tests
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm ci
- run: npm run test:pact:provider
- name: Can I Deploy?
run: |
npx pact-broker can-i-deploy \
--pacticipant=UserService \
--version=${{ github.sha }} \
--to-environment=production \
--broker-base-url=${{ secrets.PACT_BROKER_URL }} \
--broker-token=${{ secrets.PACT_BROKER_TOKEN }}
Best Practices
✅ DO
Test contracts from consumer perspective
Use matchers for flexible matching
Validate schema structure, not specific values
Version your contracts
Test error responses
Use Pact broker for contract sharing
Run contract tests in CI
Test backward compatibility
❌ DON'T
Test business logic in contract tests
Hard-code specific values in contracts
Skip error scenarios
Test UI in contract tests
Ignore contract versioning
Deploy without contract verification
Test implementation details
Mock contract tests
Tools
Pact : Consumer-driven contracts (multiple languages)
Spring Cloud Contract : JVM contract testing
OpenAPI/Swagger : API specification and validation
Postman : API contract testing
REST Assured : Java API testing
Dredd : OpenAPI/API Blueprint testing
Spectral : OpenAPI linting
Examples See also: integration-testing, api-versioning-strategy, continuous-testing for comprehensive API testing strategies.