wiremock-pact-writer
v0.1.1
Published
Write Pact V3 contract interactions as a side-effect of WireMock-backed integration tests — no separate consumer test files needed.
Downloads
298
Maintainers
Readme
wiremock-pact-writer
Write Pact V3 contract interactions as a side-effect of real WireMock-backed integration tests — no separate consumer test files, no extra frameworks.
The problem
Most Pact consumer libraries require you to write a second set of tests — the "consumer contract tests" — that replay your API calls against a mock driven by the Pact DSL. If you're already running WireMock integration tests against a real running service, this is duplicate work.
The pattern
┌─────────────────────────────────────────────────────────────────┐
│ Integration test (Jest / Vitest / Mocha / …) │
│ │
│ 1. Register WireMock stub │
│ 2. Call your service under test │
│ 3. Assert on the response ← same as before │
│ 4. writePactInteraction(…) ← records the contract as JSON │
└─────────────────────────────────────────────────────────────────┘The pact JSON is written only when WRITE_PACTS=true (e.g. in CI), so local development stays fast with zero overhead.
Installation
npm install wiremock-pact-writer
# or
yarn add wiremock-pact-writerNo runtime dependencies — only Node.js built-ins (fs, path).
Usage
1. Define project-level constants
Keep consumer / provider names in one place in your project:
// pact/config.ts
export const CONSUMER = 'my-service';
export const PROVIDERS = {
usersApi: 'users-api',
notificationsApi: 'notifications-api',
} as const;2. Return a writePact closure from stub functions
// wiremock/stubs/users/get-user.stub.ts
import { writePactInteraction } from 'wiremock-pact-writer';
import { CONSUMER, PROVIDERS } from '../../pact/config';
export async function stubGetUser(userId: string): Promise<() => Promise<void>> {
const body = { id: userId, name: 'Alice', email: '[email protected]' };
// Register your WireMock stub as usual …
await wiremock.register({ method: 'GET', endpoint: `/users/${userId}` }, { status: 200, body });
// Return the pact closure
return () =>
writePactInteraction({
consumer: CONSUMER,
provider: PROVIDERS.usersApi,
description: 'GET /users/:id - 200 user found',
providerState: 'user alice exists',
request: { method: 'GET', path: `/users/${userId}` },
response: { status: 200, headers: { 'Content-Type': 'application/json' }, body },
});
}3. Call writePact() after assertions
test('200 returns user', async () => {
const writePact = await stubGetUser('user-123');
const resp = await myService.getUser('user-123');
expect(resp.status).toBe(200);
expect(resp.body.name).toBe('Alice');
await writePact(); // ← one line; no-op locally, writes pact in CI
});Environment variables
| Variable | Default | Description |
|----------|---------|-------------|
| WRITE_PACTS | — | Set to "true" to enable writing. No-op otherwise. |
| PACT_DIR | <cwd>/pacts | Directory where pact JSON files are written. |
Matching rules
writePactInteraction automatically infers Pact V3 matching rules from the body shape:
| Value type | Matching rule |
|------------|--------------|
| string, boolean | { match: 'type' } |
| whole number | { match: 'integer' } |
| float | { match: 'decimal' } |
| array | { match: 'type', min: 1 } |
| nested object | recurse into each key |
This means provider verification will check type correctness rather than exact values, which is the right default for most contracts.
You can also use inferBodyMatchingRules directly if you need the rules for another purpose:
import { inferBodyMatchingRules } from 'wiremock-pact-writer';
const rules = inferBodyMatchingRules({ id: 'abc', count: 42 });
// {
// '$.id': { matchers: [{ match: 'type' }] },
// '$.count': { matchers: [{ match: 'integer' }] },
// }API
writePactInteraction(opts: PactInteractionOpts): Promise<void>
Appends one interaction to <PACT_DIR>/<consumer>-<provider>.json. Creates the file if it does not exist.
inferBodyMatchingRules(body: unknown, jsonPath?: string): MatchingRules
Returns Pact V3 matching rules for the given body, rooted at jsonPath (default '$').
License
MIT
