API Contract-Testing Interview Scenarios
Practice API contract-testing interview scenarios on consumer needs, provider verification, schema evolution, messaging, deployment gates, and failure triage.
API Contract-Testing Interview Scenarios
The provider adds middleName: null to a customer response. One mobile client ignores it, another rejects unknown properties, and the OpenAPI diff labels the change additive. Is the release safe? A strong contract-testing answer does not recite “additive changes are backward compatible.” It asks what each deployed consumer actually accepts and what evidence the delivery pipeline has.
Senior interviews use scenarios like this because contract testing sits between code, team ownership, and deployment risk. The candidate must distinguish schema conformance from consumer-driven contracts, place tests on the correct side of the boundary, reason about version combinations, and avoid turning a contract into a duplicate end-to-end suite.
This reference presents practical prompts, the reasoning an interviewer is listening for, and follow-up complications. It does not prescribe one tool for every architecture.
Scenario: an additive response breaks a strict consumer
The provider team wants to add an optional promotion object to GET /cart/{id}. Their OpenAPI compatibility check passes. A generated consumer deserializer is configured to reject unknown fields.
A good answer separates two claims. The provider still conforms to an evolved schema, but a deployed consumer may not tolerate the new payload. Schema compatibility policy is not proof of runtime consumer compatibility. The team needs a consumer contract or compatibility test that records the strict behavior, plus a decision about whether strict unknown-field rejection is intentional.
The best long-term repair is often to make readers tolerant of response fields they do not use. The contract should assert only fields and formats that affect consumer behavior. Over-constraining the entire response freezes the provider and produces noise.
| Candidate response | Interview signal | Follow-up question |
|---|---|---|
| “Optional means safe” | Treats schema rules as universal runtime truth | What if deserialization rejects unknown keys? |
| “Never add fields” | Avoids risk by freezing evolution | How can the provider evolve for new consumers? |
| “Verify actual consumer expectations” | Understands consumer-driven evidence | Which fields should the pact include? |
| “Run the whole UI suite” | Uses a slow indirect oracle | How will the failure identify the incompatible field? |
Listen for deployed-version awareness. Fixing the consumer on main does not protect an older mobile release already in users' hands.
Scenario: where does a consumer Pact test stop?
An order web app calls an inventory service. The interviewer asks the candidate to test the client method that requests one SKU and maps a 404 into OutOfStockError.
The consumer test should exercise the real HTTP client code against Pact's mock server, define the request and minimum response the consumer needs, and assert the mapping. It should not drive the browser, database, or provider implementation. The generated pact captures the interaction for provider verification.
This Pact JS example uses a flexible type matcher for stock count while keeping the SKU exact because the request identity matters.
import { PactV4, MatchersV3 } from '@pact-foundation/pact';
import { describe, it, expect } from 'vitest';
import { InventoryClient } from './inventory-client';
const provider = new PactV4({
consumer: 'order-web',
provider: 'inventory-api',
dir: './pacts',
});
describe('InventoryClient', () => {
it('reads available stock for a known SKU', async () => {
await provider
.addInteraction()
.given('SKU KB-42 has 7 units')
.uponReceiving('a stock request for KB-42')
.withRequest('GET', '/stock/KB-42', (builder) => {
builder.headers({ Accept: 'application/json' });
})
.willRespondWith(200, (builder) => {
builder.headers({ 'Content-Type': 'application/json' });
builder.jsonBody({ sku: 'KB-42', available: MatchersV3.integer(7) });
})
.executeTest(async (mockServer) => {
const client = new InventoryClient(mockServer.url);
await expect(client.getStock('KB-42')).resolves.toEqual({ sku: 'KB-42', available: 7 });
});
});
});
API details vary across Pact JS major versions, so an interview candidate need not memorize builder syntax. The important reasoning is stable: the consumer owns expectations, the mock is generated by the contract tool, real client code makes the request, and the interaction becomes provider-verifiable evidence.
An excellent answer also asks whether the consumer truly requires the exact Content-Type, SKU echo, integer semantics, and absence or presence of other keys. Each expectation must prevent a consumer-relevant break.
Scenario: provider states without a shared database fixture
Provider verification replays “SKU KB-42 has 7 units,” but the inventory service needs database state. The weak proposal is to point verification at a shared staging environment and hope the row exists.
Provider states are named preconditions. The provider team implements handlers that arrange those conditions locally: seed a test database, stub a downstream warehouse, or configure the service through a test-only control available only in the verification process. The consumer describes meaning, not the provider's SQL.
The provider owns how to create the state because it owns the implementation. State setup should be idempotent and isolated per interaction. Shared staging data makes verification order-dependent and prevents reliable reproduction.
| State approach | Contract ownership | Reliability assessment |
|---|---|---|
| Consumer sends SQL to provider DB | Consumer knows provider internals | Reject, tight coupling and unsafe access |
| Provider state handler seeds local repository | Provider controls implementation details | Strong default |
| Long-lived staging fixture | Nobody owns freshness | Fragile under concurrent runs |
| Stub downstream system at provider boundary | Provider defines relevant dependency behavior | Useful when verifying HTTP surface only |
Ask what happens when state setup fails. The verifier must report an infrastructure or setup error, not reinterpret it as an API mismatch.
Scenario: publishing and verifying are green on separate commits
The consumer's latest contract passed against provider commit A. Provider commit B changes a response and has never verified that consumer version. Both repositories show green default-branch builds. Can B deploy?
No conclusion follows from two independent green badges. Deployment safety is a relation between application versions. A broker matrix records which consumer and provider versions were verified. A deployment check such as Pact Broker's can-i-deploy evaluates relevant version combinations for an environment or deployment target.
A senior answer mentions unique version identifiers, usually commit SHAs, and branch or environment metadata. Publishing every build as latest destroys traceability. The pipeline should publish consumer contracts, verify changed provider versions against relevant consumer versions, publish verification results, and query the matrix before deployment.
The interviewer can complicate the scenario with a feature branch consumer. The answer should avoid verifying every historical contract forever while still covering deployed consumers and versions about to deploy. Broker selectors and deployment records solve an evidence-selection problem; “always fetch latest” does not.
Scenario: request validation passes but semantics changed
POST /transfers still accepts amount: 100, returns 201, and conforms to OpenAPI. The provider changed the unit from cents to major currency units. Which test catches it?
A schema contract cannot infer business meaning from an unchanged number type. A consumer-driven example may catch it if the consumer depends on a specific response or downstream representation, but contract tests are not a substitute for provider functional tests. The provider should have domain tests proving monetary semantics. The API description should make units explicit, perhaps through naming such as amountMinor, examples, and documentation.
The strongest response assigns tests according to responsibility:
- Schema validation checks shape and constraints.
- Consumer contracts check interactions the consumer relies on.
- Provider tests check calculations and business rules.
- A small integration or journey test checks critical wiring.
Claiming that Pact proves all provider behavior is a red flag. Contract verification proves the provider can satisfy recorded interactions under arranged states.
Scenario: should an exact value or matcher be used?
The consumer receives { "id": "ord_921", "status": "pending" }. A candidate proposes regex matching every string.
Matchers should express actual freedom. The ID may require only a non-empty string or a known prefix if parsing depends on it. Status may need exact equality because the consumer branches on pending. A regex that accepts any lowercase word would allow a breaking awaiting_review value. Conversely, exact matching a generated ID can over-constrain the provider for no consumer benefit.
Ask “what consumer bug would this expectation catch?” If no meaningful bug emerges, the expectation probably does not belong in the contract. If the consumer has exhaustive enum handling, each relevant status and the behavior for unknown values deserve deliberate treatment.
The API contract testing for microservices guide explores matcher choice, provider ownership, and broker workflow in an implementation context.
Scenario: a field is removed after telemetry shows no use
The provider wants to remove legacyCode. Search finds no reference in current repositories, and API telemetry shows few responses requesting it. Is that enough?
Repository search misses external consumers, old deployed binaries, dynamic access, and copied integrations. Response telemetry may show delivery, not consumption. A contract broker can show declared expectations for participating consumers, but absence of a contract is not proof that no consumer exists.
The answer should ask about API audience, inventory, deprecation policy, consumer registration, and supported version window. For an internal API with enforced contract participation, broker evidence plus deployment records may be strong. For a public API, use versioning and a communicated deprecation period; consumer-driven contracts cannot enumerate unknown clients.
This scenario tests whether the candidate understands organizational scope. Tools provide evidence only for integrations that adopted them.
Scenario: asynchronous message contracts
An order service publishes OrderAccepted to a broker. The fulfillment consumer needs orderId, shipping address, and item quantities. The interviewer asks whether the contract test should start Kafka.
A message contract is transport-agnostic at its core. The consumer test invokes message-handling logic with a contract-generated payload and records required contents and metadata. Provider verification asks the producer to generate a message for a stated condition and compares it to the contract. Kafka configuration, partitions, delivery, retries, and serialization framing require separate integration tests.
The candidate should distinguish payload compatibility from delivery semantics. A passing message pact does not prove the topic name is correct, permissions allow publish and consume, keys preserve ordering, or the consumer commits offsets safely.
Useful follow-ups include:
- What if the producer adds a field? The consumer should ignore fields it does not use unless its deserializer is strict.
- What if an enum gains a value? Compatibility depends on consumer handling.
- What if JSON becomes Avro? That is a transport and serialization migration requiring coordinated evidence beyond a payload example.
- What if events arrive twice? Idempotency is a consumer behavior test, not merely a schema assertion.
Scenario: OpenAPI versus consumer-driven contracts
An interviewer asks the candidate to choose one. Reject the false binary. OpenAPI is provider-oriented documentation and can support linting, generation, request validation, and broad compatibility rules. Consumer-driven contracts capture concrete expectations from known consumers and verify them against provider versions. They overlap but answer different questions.
| Question | OpenAPI-centered check | Consumer-driven check |
|---|---|---|
| Does the implementation match the declared operation schema? | Strong | Indirect and example-focused |
| Does a known consumer depend on this response field? | Usually unknown | Explicit when well authored |
| Is every documented status represented? | Can analyze description coverage | Only statuses consumers exercise |
| Can an unknown external client tolerate a change? | Policy estimate | No, unknown consumer has no pact |
| Can this provider version deploy with recorded consumer versions? | Needs additional version system | Broker matrix is designed for it |
Mature teams may generate provider tests from OpenAPI and run Pact verification in the same pipeline. Duplicating every schema field in every pact is not the goal.
Scenario: provider verification fails after a harmless timestamp change
The provider changes createdAt from a fixed fixture to the current ISO timestamp. Pacts fail because the consumer expected 2026-01-10T10:00:00Z exactly.
First determine whether exact time matters. If the consumer parses any RFC 3339 timestamp, use an appropriate matcher with a valid example. If it compares a business date or relies on timezone normalization, the expectation may need more precision. Do not weaken the matcher merely to make CI green.
Then inspect where the over-constraint arose. Consumer tests should contain representative examples but distinguish examples from matching rules. A generated SDK may impose a format constraint even if hand-written code does not. Test actual client behavior.
The candidate should propose changing the consumer test and publishing a new pact, not manually editing the generated pact JSON. Generated contracts are build artifacts; hand edits separate them from executable consumer evidence.
Scenario: a contract test is flaky
Contract tests should be deterministic, so flakiness deserves architectural suspicion. Common causes include shared provider state, random examples embedded as exact expectations, reliance on live downstream services, non-unique pact version publication, and parallel verification mutating one database.
A good triage sequence identifies whether the failure is consumer test generation, broker retrieval, state setup, provider response, or result publication. Capture the interaction and version coordinates. Re-run locally against an isolated provider with deterministic states. Do not simply add retries around a genuine mismatch, because a contract either matches that version pair or it does not.
Network retries may be appropriate for broker transport with bounded policy, but verification results must still refer to the exact provider and consumer versions. Retrying the entire build against a moving “latest” contract changes the question mid-investigation.
A runnable provider-verification boundary
The following Pact JS verifier configuration shows the responsibilities interview answers should mention: provider identity, exact provider version, broker source, publication only in CI, and provider-owned state handlers. The application is assumed to be started locally at the supplied base URL.
import { Verifier } from '@pact-foundation/pact';
import { resetInventory, seedInventory } from './verification-data';
const providerVersion = process.env.GIT_SHA;
if (!providerVersion) throw new Error('GIT_SHA is required for provider verification');
await new Verifier({
provider: 'inventory-api',
providerBaseUrl: 'http://127.0.0.1:4010',
pactBrokerUrl: process.env.PACT_BROKER_URL,
pactBrokerToken: process.env.PACT_BROKER_TOKEN,
providerVersion,
publishVerificationResult: Boolean(process.env.CI),
stateHandlers: {
'SKU KB-42 has 7 units': async () => {
await resetInventory();
await seedInventory({ sku: 'KB-42', available: 7 });
},
'SKU KB-42 does not exist': async () => {
await resetInventory();
},
},
}).verifyProvider();
In real code, consumer version selectors or pending and work-in-progress pact policies should be configured according to the broker workflow. The snippet avoids inventing selectors without organizational context. An interview candidate earns credit for raising that choice rather than claiming one selector fits every branching model.
How to answer design questions under interview pressure
Use a compact reasoning frame. Name the consumer and provider. State the consumer behavior at risk. Choose whether the evidence is schema, consumer interaction, provider functionality, or transport integration. Explain who owns setup and where it runs. Finally, describe how versioned results gate deployment.
For debugging questions, trace the lifecycle: consumer test -> pact publication -> contract selection -> provider state -> replay -> matching -> verification publication -> deployment query. This is more convincing than listing tool commands.
The test automation interview questions reference covers broader coding, framework, and strategy discussions. Contract-testing scenarios reward the same habit: make assumptions explicit and tie each test to a failure it can uniquely explain.
Avoid these interview traps:
- Calling a shared mock server a consumer contract without generated, provider-verifiable evidence.
- Asserting every response field “for safety,” which blocks harmless provider evolution.
- Believing additive schema changes are safe for all runtimes.
- Using production or shared staging data for provider states.
- Publishing verification under a mutable version label.
- Treating message compatibility as proof of broker delivery behavior.
- Claiming contracts replace provider business tests or a small number of end-to-end journeys.
- Assuming the broker knows consumers that never publish contracts.
The strongest candidates are not absolutists. They know exactly what a green contract proves, what it leaves unproven, and which team can act on a failure.
Frequently Asked Questions
What is the shortest accurate distinction between schema and consumer-driven contract testing?
Schema testing checks an implementation or payload against a provider-declared shape. Consumer-driven testing records behavior a known consumer relies on and verifies that a provider version can satisfy it.
Should a Pact consumer test assert the entire provider response?
No. Assert fields, values, and formats whose change would affect the consumer. Extra expectations create false failures and restrict safe provider evolution.
Who implements provider state setup?
The provider team does. The consumer names the business precondition, while provider-owned handlers arrange it using local implementation knowledge and isolated dependencies.
Can contract tests replace end-to-end API tests?
They can replace many slow integration checks at service boundaries, but they do not prove deployment wiring, network policy, message delivery, full business workflows, or all provider functionality. Keep focused tests for those risks.
Why are commit-based application versions important in a Pact Broker?
Verification is a relationship between specific consumer and provider versions. Immutable commit identifiers let the broker matrix and deployment checks answer which exact combinations have evidence, unlike a moving label such as latest.