Skip to content
Back to skills

Test Contract Testing

ASecurity

> Verify service compatibility using Pact consumer-provider contract tests

  • 20 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 11, 2026
testingtypescriptbashtestinggitapi

Works with

  • cli
  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 2 files and shows the line behind each finding

Scanned September 11, 2026

npx -y skills add Intense-Visions/harness-engineering --skill test-contract-testing --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Test Contract Testing?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Test Contract Testing
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/intense-visions-test-contract-testing/badge)](https://www.skillsdirectory.com/skills/intense-visions-test-contract-testing)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
# Test Contract Testing

> Verify service compatibility using Pact consumer-provider contract tests

## When to Use

- Verifying that two services (consumer and provider) agree on API format
- Preventing breaking changes when services are deployed independently
- Testing service integration without running all services together
- Replacing fragile end-to-end tests across service boundaries

## Instructions

1. **Install Pact:**

```bash
npm install -D @pact-foundation/pact
```

2. **Write a consumer test** — define what the consumer expects from the provider:

```typescript
import { PactV4 } from '@pact-foundation/pact';

const provider = new PactV4({
  consumer: 'OrderService',
  provider: 'UserService',
});

describe('UserService API', () => {
  it('returns user by ID', async () => {
    await provider
      .addInteraction()
      .given('a user exists with ID 123')
      .uponReceiving('a request for user 123')
      .withRequest('GET', '/api/users/123', (builder) => {
        builder.headers({ Accept: 'application/json' });
      })
      .willRespondWith(200, (builder) => {
        builder.headers({ 'Content-Type': 'application/json' }).jsonBody({
          id: '123',
          name: 'Alice',
          email: 'alice@example.com',
        });
      })
      .executeTest(async (mockServer) => {
        const client = new UserClient(mockServer.url);
        const user = await client.getUser('123');

        expect(user).toEqual({
          id: '123',
          name: 'Alice',
          email: 'alice@example.com',
        });
      });
  });
});
```

3. **Generate the pact file** — running the consumer test creates a contract file (e.g., `pacts/OrderService-UserService.json`).

4. **Verify on the provider side:**

```typescript
import { Verifier } from '@pact-foundation/pact';

describe('Pact Verification', () => {
  it('validates the OrderService contract', async () => {
    const verifier = new Verifier({
      providerBaseUrl: 'http://localhost:3001',
      pactUrls: ['./pacts/OrderService-UserService.json'],
      stateHandlers: {
        'a user exists with ID 123': async () => {
          await seedUser({ id: '123', name: 'Alice', email: 'alice@example.com' });
        },
      },
    });

    await verifier.verifyProvider();
  });
});
```

5. **Use a Pact Broker** for sharing contracts across repos:

```typescript
// Consumer publishes
await publishPacts({
  pactFilesOrDirs: ['./pacts'],
  pactBroker: 'https://your-broker.pactflow.io',
  consumerVersion: process.env.GIT_SHA,
  tags: ['main'],
});

// Provider verifies from broker
const verifier = new Verifier({
  providerBaseUrl: 'http://localhost:3001',
  pactBrokerUrl: 'https://your-broker.pactflow.io',
  providerVersionTags: ['main'],
  publishVerificationResult: true,
});
```

6. **State handlers** set up test data for each interaction:

```typescript
stateHandlers: {
  'no users exist': async () => {
    await db.user.deleteMany();
  },
  'a user exists with ID 123': async () => {
    await db.user.upsert({
      where: { id: '123' },
      create: { id: '123', name: 'Alice', email: 'alice@example.com' },
      update: {},
    });
  },
},
```

7. **Use matchers** for flexible contract validation:

```typescript
import { like, eachLike, regex } from '@pact-foundation/pact';

.jsonBody({
  id: like('123'),                    // any string
  name: like('Alice'),                // any string
  email: regex(/.*@.*/, 'a@b.com'),   // matches regex
  posts: eachLike({ title: like('Post') }), // array of objects
})
```

## Details

Contract testing verifies that two services agree on the format of their communication. The consumer defines its expectations as a "contract," and the provider verifies it can fulfill that contract.

**Consumer-driven contracts:** The consumer writes the contract because it knows what it needs. The provider verifies it can deliver. This ensures the provider does not make breaking changes that affect consumers.

**Contract vs integration vs E2E:**

- **Contract** — verifies the API shape (request/response format) without running both services
- **Integration** — tests actual communication between running services
- **E2E** — tests the full user journey across all services

Contract tests are faster and more focused than integration tests but do not verify business logic.

**Pact workflow:**

1. Consumer writes tests → generates pact file (contract)
2. Pact file is shared (Pact Broker or file system)
3. Provider runs verification tests against the pact file
4. If verification fails, the provider has a breaking change

**Trade-offs:**

- Contract tests catch breaking API changes early — but do not test business logic
- Consumer-driven contracts align with consumer needs — but require provider teams to run verification
- Pact Broker enables cross-repo sharing — but adds infrastructure to maintain
- Matchers provide flexibility — but overly flexible matchers can miss real breaking changes

## Source

https://docs.pact.io/

## Process

1. Read the instructions and examples in this document.
2. Apply the patterns to your implementation, adapting to your specific context.
3. Verify your implementation against the details and edge cases listed above.

## Harness Integration

- **Type:** knowledge — this skill is a reference document, not a procedural workflow.
- **No tools or state** — consumed as context by other skills and agents.

## Success Criteria

- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.

Files in this skill

  • SKILL.md5.5 KB
  • skill.yaml773 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.