Your consumer-driven contract test passes in CI, but production rejects a request because a supposedly optional field is missing. What did the contract testing actually miss?
The contract captured the shape the consumer sends, not the rules the provider enforces. Optionality lived in the schema while the real requirement lived in provider validation, and no recorded example ever omitted the field. Contract tests only verify interactions they contain, so add the negative example and verify against real code.
What the interviewer is scoring
- Whether the candidate understands a contract only covers the interactions it explicitly contains
- That the gap between schema-level optionality and provider-side validation is identified
- Does the answer question what the provider verification ran against - a mock, a stub, or the real handler
- Whether deployment version skew is raised, so the verified provider version may not be the deployed one
- That the candidate proposes adding the negative case rather than only fixing the field
- Whether they distinguish contract testing from schema validation and from end-to-end testing, and say what each catches
- Does the answer address conditional requirements, where a field is optional except when another field has a value
Answer
Short answer
A contract test verifies the interactions it contains and nothing else. If the consumer's contract never included an example that omits the field, no test ever asked the provider what happens when it is absent — so "optional" was an assumption written into a schema, never a behaviour anyone verified. The provider's validation says otherwise, and production is the first place the two met.
Contracts cover examples, not the space of inputs
This is the misconception worth naming first, because it explains the whole failure. A consumer-driven contract is a record of concrete interactions: for this request, expect this response. It is not a specification of the endpoint and it does not explore the input space. If every example the consumer recorded happened to include promo_code, the contract asserts nothing whatsoever about requests without it.
Marking the field optional in an OpenAPI schema does not change that. The schema says the shape permits absence. The provider's handler may still reject the request in validation code, or accept it and fail deeper when business logic dereferences it. Schema optionality and runtime requirement are different claims, and only one of them was tested.
// Schema says: promo_code is optional.
// Provider says otherwise, in code the contract never reached:
if (req.promoCode() == null && req.channel() == Channel.PARTNER) {
throw new ValidationException("promo_code required for partner channel");
}
Note the shape of that condition. The field is not unconditionally required — it is required given another value. Conditional requirements are the most common version of this bug, because they are almost impossible to express in a schema and trivially easy to write in a handler.
Check what the provider verification actually ran against
The second thing to establish is whether the provider side of the verification exercised real code. Contract testing has a well-known failure mode where the provider verification replays the consumer's expectations against a controller whose service layer is stubbed. That confirms serialisation and routing and skips every validation rule that matters.
If the provider verification mocks the layer containing the validation, the contract is verifying that your mock agrees with your contract — which is always true and means nothing. Provider verification should hit the real handler with real validation, using provider states to set up data rather than to bypass logic.
Then check version skew
Even a correct contract fails to protect you if the verified version is not the deployed one. Contract testing gives a guarantee of the form "consumer version X works against provider version Y". If the provider has deployed version Z since verification, that guarantee has expired.
This is what a broker's can-i-deploy check exists for: before deploying, ask whether the version about to ship has been verified against every consumer version currently in the target environment. Teams that publish contracts but never gate deployment on verification results have the paperwork and none of the safety. If the provider added the validation rule and deployed without re-verifying, that is the actual root cause, and it is a process gap rather than a test gap.
Fixing it properly
Add the negative interaction to the contract. The consumer records an example with promo_code absent and the expected response — either a success, if that is the agreed behaviour, or a 400 with a specific error shape if it is not. Now the provider's verification will fail the moment anyone adds a rule that contradicts it. The important part is that this is not "one more test"; it converts an assumption into a verified interaction.
Make the conditional rule explicit. For "required when channel is PARTNER", the contract needs two interactions: partner channel without the field, and non-partner channel without the field. Each pins one branch. Schemas struggle to express this; examples handle it naturally, which is a genuine argument for example-based contracts over schema-only compatibility checking.
Gate deployment on verification. Wire can-i-deploy into the provider's pipeline so a rule change that breaks a live consumer blocks the deploy rather than reaching production.
Knowing what each tool actually catches
An interviewer will often push on whether contract testing was the right choice at all, so it is worth being able to place the three tools:
Schema validation catches structural incompatibility — a renamed field, a type change, a removed property. It is cheap, runs on every change, and knows nothing about behaviour.
Contract testing catches disagreement about the interactions that were recorded, including status codes and error bodies. It scales across many consumers without a shared environment, and its blind spot is precisely the interaction nobody thought to record.
Integration testing against a real deployed provider explores whatever the test exercises, including paths nobody predicted. It is slower, needs an environment, and is flakier — but it is the only one of the three that can find a problem you did not anticipate.
The honest conclusion is that contract testing did not fail here so much as it was asked a question it does not answer. A contract is a shared agreement about known cases; it is not a search for unknown ones. Teams that expect it to be exhaustive are the ones surprised in production, and saying that plainly is usually the strongest part of the answer.
© 2026 Preptima. Originally published at preptima.com.
Likely follow-ups
- Where exactly would you add a test, and what would it assert?
- The provider verification runs against a stubbed service layer. What does that invalidate?
- How does a broker with can-i-deploy change this outcome?
- The field is optional unless currency is USD. How do you express that in a contract?
- When is contract testing the wrong tool and you should just run an integration test?
Related questions
- Two teams own two services that talk to each other. How do you stop one breaking the other without running both together in a shared environment?mediumAlso on contract-testing and api-testing4 min
- You own the API test suite for a service from scratch. How do you structure it, and what do you mock?hardAlso on api-testing and contract-testing8 min
- Your API suite runs on every commit against a shared database. How do you keep the tests from corrupting each other's data?hardAlso on api-testing6 min
- You need to add one field to an event and remove another, and five teams consume it. How does that roll out?hardAlso on contract-testing6 min