DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Designing Contract Testing with Claude Code: Pact, API Compatibility, Consumer-Driven

Introduction

When changing APIs in microservices, you want to detect if consumers (callers) will break. Contract testing automatically verifies that provider (API provider) changes don't impact consumers. Generate designs with Claude Code.


CLAUDE.md Contract Testing Rules

## Contract Testing Design Rules

### Pact (Consumer-Driven Contract Testing)
- Consumer side defines the contract (expected API shape)
- Provider side verifies it meets the contract
- Share and version contracts via PactBroker

### Test Targets
- Verify existence of response fields
- Verify response data types
- Verify HTTP status codes
- Don't verify exact values (avoid environment dependencies)

### CI/CD Integration
- Consumer: generate contract and publish to PactBroker on PR
- Provider: verify contract before deploy
- can-i-deploy check: confirm safe to deploy
Enter fullscreen mode Exit fullscreen mode

Generated Contract Tests

// tests/contract/consumer/orderService.pact.ts (defined by BFF)
const { like, eachLike, integer, string, iso8601DateTimeWithMillis } = Matchers;

describe('OrderService Contract', () => {
  test('returns recent orders for a user', async () => {
    await provider.addInteraction({
      state: 'user has 3 orders',
      uponReceiving: 'a request for recent orders',
      withRequest: {
        method: 'GET',
        path: '/orders',
        query: { userId: 'user-123', limit: '5' },
        headers: { 'X-Internal-Token': like('some-token') },
      },
      willRespondWith: {
        status: 200,
        body: {
          orders: eachLike({
            id: string('order-id-1'),
            userId: string('user-123'),
            status: string('pending'),
            total: integer(5000),         // Verify "is integer", not exact value
            createdAt: iso8601DateTimeWithMillis('2025-01-01T00:00:00.000Z'),
          }),
        },
      },
    });

    const client = new OrderServiceClient(provider.mockService.baseUrl);
    const result = await client.getRecentOrders('user-123', 5);

    expect(result[0]).toMatchObject({
      id: expect.any(String),
      userId: expect.any(String),
      status: expect.any(String),
      total: expect.any(Number),
    });
  });
});
Enter fullscreen mode Exit fullscreen mode
// tests/contract/provider/orderService.verification.ts (OrderService side verifies)
test('verify pacts from PactBroker', async () => {
  await new Verifier({
    provider: 'OrderService',
    providerBaseUrl: 'http://localhost:8080',
    pactBrokerUrl: process.env.PACT_BROKER_URL!,
    pactBrokerToken: process.env.PACT_BROKER_TOKEN!,

    stateHandlers: {
      'user has 3 orders': async () => {
        await prisma.order.createMany({
          data: [
            { id: 'order-1', userId: 'user-123', status: 'pending', total: 5000 },
            { id: 'order-2', userId: 'user-123', status: 'confirmed', total: 3000 },
          ],
        });
      },
    },

    publishVerificationResult: true,
    providerVersion: process.env.APP_VERSION ?? '1.0.0',
  }).verifyProvider();
});
Enter fullscreen mode Exit fullscreen mode

GitHub Actions Integration

jobs:
  consumer-contract:
    steps:
      - run: npm run test:contract:consumer
      - name: Publish pacts to PactBroker
        run: |
          npx pact-broker publish ./pacts \
            --broker-base-url ${{ secrets.PACT_BROKER_URL }} \
            --broker-token ${{ secrets.PACT_BROKER_TOKEN }} \
            --consumer-app-version ${{ github.sha }}

  can-i-deploy:
    steps:
      - name: Check if safe to deploy
        run: |
          npx pact-broker can-i-deploy \
            --pacticipant OrderService \
            --version ${{ github.sha }} \
            --to production
Enter fullscreen mode Exit fullscreen mode

Summary

Design Contract Testing with Claude Code:

  1. CLAUDE.md — Consumer-Driven, PactBroker sharing, CI/CD integration
  2. Consumer defines contracts (document expectations for the provider)
  3. like()/eachLike() — verify type/structure, not exact values (avoid env dependencies)
  4. can-i-deploy — verify no consumer impact before deploy

Review contract test designs with **Code Review Pack (¥980)* using /code-review at prompt-works.jp*

myouga (@myougatheaxo) — Axolotl VTuber.

Top comments (0)