pact-contract-testing
You are pact-contract-testing - a specialized skill for consumer-driven contract testing with the Pact framework, enabling reliable API integration testing between services.
Overview
This skill enables AI-powered contract testing including:
- Generating consumer contracts (Pact files)
- Configuring Pact Broker publishing
- Provider verification execution
- Breaking change detection
- Webhook integration for CI/CD
- Can-i-deploy checks
- Contract versioning management
- Bidirectional contract testing
Prerequisites
- Node.js, Java, or Python environment
- Pact library for your language
- Pact Broker (self-hosted or PactFlow)
- CI/CD pipeline access
- Consumer and provider applications
Capabilities
1. Consumer Contract Generation
Create consumer-side contracts with Pact JS:
import { PactV3, MatchersV3 } from '@pact-foundation/pact';
const { like, eachLike, regex } = MatchersV3;
const provider = new PactV3({
consumer: 'frontend-app',
provider: 'user-service',
logLevel: 'info'
});
describe('User API Contract', () => {
it('should return user by ID', async () => {
// Arrange: Define expected interaction
await provider
.given('a user with ID 123 exists')
.uponReceiving('a request for user 123')
.withRequest({
method: 'GET',
path: '/api/users/123',
headers: {
Accept: 'application/json',
Authorization: regex(/Bearer .+/, 'Bearer token123')
}
})
.willRespondWith({
status: 200,
headers: {
'Content-Type': 'application/json'
},
body: {
id: like(123),
email: like('user@example.com'),
name: like('John Doe'),
createdAt: like('2024-01-15T10:30:00Z'),
roles: eachLike('user')
}
});
// Act & Assert: Execute test
await provider.executeTest(async (mockServer) => {
const response = await fetch(`${mockServer.url}/api/users/123`, {
headers: {
Accept: 'application/json',
Authorization: 'Bearer token123'
}
});
expect(response.status).toBe(200);
const user = await response.json();
expect(user.id).toBe(123);
});
});
it('should return 404 for non-existent user', async () => {
await provider
.given('user 999 does not exist')
.uponReceiving('a request for non-existent user')
.withRequest({
method: 'GET',
path: '/api/users/999'
})
.willRespondWith({
status: 404,
body: {
error: like('User not found'),
code: like('USER_NOT_FOUND')
}
});
await provider.executeTest(async (mockServer) => {
const response = await fetch(`${mockServer.url}/api/users/999`);
expect(response.status).toBe(404);
});
});
});
2. Provider Verification
Verify provider against contracts:
import { Verifier } from '@pact-foundation/pact';
const verifier = new Verifier({
provider: 'user-service',
providerBaseUrl: 'http://localhost:3000',
// Fetch pacts from broker
pactBrokerUrl: 'https://your-broker.pactflow.io',
pactBrokerToken: process.env.PACT_BROKER_TOKEN,
// Provider version
providerVersion: process.env.GIT_COMMIT || '1.0.0',
providerVersionBranch: process.env.GIT_BRANCH || 'main',
// State handlers
stateHandlers: {
'a user with ID 123 exists': async () => {
// Set up test data
await db.users.create({ id: 123, email: 'user@example.com', name: 'John Doe' });
},
'user 999 does not exist': async () => {
// Ensure user doesn't exist
await db.users.delete(999);
}
},
// Publish results
publishVerificationResult: true,
enablePending: true,
includeWipPactsSince: '2024-01-01'
});
describe('Provider Verification', () => {
beforeAll(async () => {
// Start provider service
await startServer();
});
afterAll(async () => {
await stopServer();
});
it('should verify all consumer contracts', async () => {
await verifier.verifyProvider();
});
});
3. Pact Broker Publishing
Publish contracts to Pact Broker:
import { Publisher } from '@pact-foundation/pact';
const publisher = new Publisher({
pactFilesOrDirs: ['./pacts'],
pactBroker: 'https://your-broker.pactflow.io',
pactBrokerToken: process.env.PACT_BROKER_TOKEN,
consumerVersion: process.env.GIT_COMMIT || '1.0.0',
branch: process.env.GIT_BRANCH || 'main',
tags: [process.env.GIT_BRANCH || 'main']
});
await publisher.publishPacts();
4. Can-I-Deploy Check
Verify deployment safety:
# Check if consumer can be deployed
pact-broker can-i-deploy \
--pacticipant frontend-app \
--version $(git rev-parse HEAD) \
--to-environment production \
--broker-base-url https://your-broker.pactflow.io \
--broker-token $PACT_BROKER_TOKEN
# Check if provider can be deployed
pact-broker can-i-deploy \
--pacticipant user-service \
--version $(git rev-parse HEAD) \
--to-environment production \
--broker-base-url https://your-broker.pactflow.io \
--broker-token $PACT_BROKER_TOKEN
# Record deployment
pact-broker record-deployment \
--pacticipant user-service \
--version $(git rev-parse HEAD) \
--environment production \
--broker-base-url https://your-broker.pactflow.io \
--broker-token $PACT_BROKER_TOKEN
5. CI/CD Integration
GitHub Actions workflow:
name: Contract Tests
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
PACT_BROKER_URL: https://your-broker.pactflow.io
PACT_BROKER_TOKEN: ${{ secrets.PACT_BROKER_TOKEN }}
jobs:
consumer-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run consumer contract tests
run: npm run test:contract:consumer
- name: Publish pacts
run: |
npx pact-broker publish ./pacts \
--consumer-app-version ${{ github.sha }} \
--branch ${{ github.ref_name }} \
--broker-base-url $PACT_BROKER_URL \
--broker-token $PACT_BROKER_TOKEN
provider-verification:
runs-on: ubuntu-latest
needs: consumer-tests
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Start provider
run: npm run start:test &
- name: Verify provider
run: npm run test:contract:provider
can-i-deploy:
runs-on: ubuntu-latest
needs: [consumer-tests, provider-verification]
if: github.ref == 'refs/heads/main'
steps:
- name: Can I deploy?
run: |
docker run --rm pactfoundation/pact-cli \
broker can-i-deploy \
--pacticipant frontend-app \
--version ${{ github.sha }} \
--to-environment production \
--broker-base-url $PACT_BROKER_URL \
--broker-token $PACT_BROKER_TOKEN
6. Webhook Configuration
Set up Pact Broker webhooks:
# Trigger provider verification on consumer change
pact-broker create-webhook \
'https://api.github.com/repos/org/provider-repo/dispatches' \
--request=POST \
--header 'Accept: application/vnd.github.v3+json' \
--header 'Authorization: Bearer ${GITHUB_TOKEN}' \
--data '{"event_type": "contract_requiring_verification", "client_payload": {"pact_url": "${pactbroker.pactUrl}"}}' \
--description "Trigger provider verification on contract change" \
--contract-content-changed \
--broker-base-url https://your-broker.pactflow.io \
--broker-token $PACT_BROKER_TOKEN
7. Bidirectional Contract Testing
Use with OpenAPI specifications: