Назад към всички

pact-contract-testing

// Consumer-driven contract testing with Pact framework. Generate consumer contracts, configure Pact Broker publishing, execute provider verification, detect breaking changes, and integrate with CI/CD pipelines.

$ git log --oneline --stat
stars:384
forks:73
updated:March 4, 2026
SKILL.mdreadonly
SKILL.md Frontmatter
namepact-contract-testing
descriptionConsumer-driven contract testing with Pact framework. Generate consumer contracts, configure Pact Broker publishing, execute provider verification, detect breaking changes, and integrate with CI/CD pipelines.
allowed-toolsBash(*) Read Write Edit Glob Grep WebFetch
metadata[object Object]

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:

// Provider publishes OpenAPI spec
import { PactV3 } from '@pact-foundation/pact';

// Consumer tests against provider's published OpenAPI
const provider = new PactV3({
  consumer: 'frontend-app',
  provider: 'user-service',
  pactBrokerUrl: 'https://your-broker.pactflow.io',
  pactBrokerToken: process.env.PACT_BROKER_TOKEN
});

// Provider publishes OAS
// pact-broker publish-provider-contract \
//   openapi.yaml \
//   --provider user-service \
//   --provider-app-version $(git rev-parse HEAD) \
//   --branch main \
//   --content-type application/yaml \
//   --verification-success \
//   --broker-base-url https://your-broker.pactflow.io \
//   --broker-token $PACT_BROKER_TOKEN

8. Matchers and Generators

Use flexible matching:

import { MatchersV3 } from '@pact-foundation/pact';

const {
  like,           // Type matching
  eachLike,       // Array matching
  regex,          // Regex matching
  integer,        // Integer type
  decimal,        // Decimal type
  boolean,        // Boolean type
  string,         // String type
  datetime,       // ISO datetime
  uuid,           // UUID format
  ipv4Address,    // IPv4 address
  email,          // Email format
  atLeastOneLike, // At least one item matching
  atMostLike,     // At most N items matching
  constrainedArrayLike // Min/max array
} = MatchersV3;

const userContract = {
  id: uuid(),
  email: email('test@example.com'),
  name: string('John Doe'),
  age: integer(25),
  balance: decimal(100.50),
  isActive: boolean(true),
  createdAt: datetime("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"),
  roles: eachLike('user'),
  preferences: like({
    theme: 'dark',
    notifications: true
  }),
  tags: constrainedArrayLike('tag', 1, 5)
};

MCP Server Integration

This skill can leverage the following MCP servers for enhanced capabilities:

ServerDescriptionInstallation
PactFlow MCP ServerAI-powered contract testing in IDEPactFlow Blog

Best Practices

  1. Consumer-first - Start with consumer expectations
  2. Provider states - Use meaningful state names
  3. Versioning - Use git commit hashes for versions
  4. CI integration - Automate all contract testing
  5. Can-i-deploy - Always check before deployment
  6. Pending pacts - Enable for new consumers
  7. WIP pacts - Include work-in-progress pacts
  8. Branch awareness - Tag pacts with branch names

Process Integration

This skill integrates with the following processes:

  • contract-testing.js - All phases of contract testing
  • api-testing.js - API contract validation
  • continuous-testing.js - CI/CD contract integration
  • quality-gates.js - Contract verification gates

Output Format

When executing operations, provide structured output:

{
  "operation": "verify",
  "provider": "user-service",
  "providerVersion": "abc123",
  "consumers": [
    {
      "name": "frontend-app",
      "version": "def456",
      "status": "passed",
      "interactions": 5,
      "passed": 5,
      "failed": 0
    }
  ],
  "canDeploy": true,
  "environment": "production",
  "verificationUrl": "https://broker.pactflow.io/verifications/123"
}

Error Handling

  • Handle missing provider states gracefully
  • Provide clear mismatch descriptions
  • Log full request/response on failures
  • Support retry for transient failures
  • Document breaking changes clearly

Constraints

  • Contracts represent consumer needs, not full API
  • Provider states must be reproducible
  • Broker must be accessible from CI/CD
  • Version management is critical
  • Breaking changes require coordination