Schema Compatibility
This page explains how the plugin identifies, embeds, and resolves Avro schemas across a Pact interaction’s lifecycle, and what that means when your schema changes over time.
Schemas are embedded in the Pact file, by hash
When a consumer test configures an interaction, the plugin hashes the resolved Avro schema (MD5, base16-encoded) and uses that hash as a schema key. The full schema text is written both into the Pact file’s pact-wide plugin configuration under that key, and directly onto the interaction itself, alongside the record name and key:
-
pluginConfiguration.interactionConfiguration→{ "record": "<RecordName>", "schemaKey": "<schemaKey>", "avroSchema": "<schema JSON>" } -
pluginConfiguration.pactConfiguration→{ "<schemaKey>": { "avroSchema": "<schema JSON>" } }
During verification, the plugin does not re-read the .avsc file. It reads the interaction’s own avroSchema first, falling back to looking the schema up by schemaKey in the Pact file’s pactConfiguration only if the interaction doesn’t carry it (see Troubleshooting & FAQ for why a pact-jvm consumer test can produce a Pact file without one). Either way, the schema a consumer test was written against travels with the contract, and a provider is verified against the exact schema the consumer used, not whatever .avsc file happens to be on disk at verification time.
A practical consequence: two interactions built from schemas that produce different serialised text get two independent entries in the Pact file, even if both use the same record name. Editing a .avsc file changes its hash, so previously generated Pact files are unaffected until the consumer test is re-run against the new schema.
Record resolution
A schema file can define a single record or a union/array of record definitions. The plugin resolves the concrete record for an interaction by matching pact:record-name (consumer side) or the record=<Name> suffix on the interaction’s content type (provider side) against the record names in the schema:
-
If the schema is a single record and its name does not match, configuration fails with
Record '<name>' was not found in avro Schema provided. -
If the schema is a union and no member record matches, it fails with
Avro union schema didn’t contain record: '<name>'. -
During verification, the actual and expected bodies must resolve to the same record name, or comparison fails with
Record names don’t match, actual: '<a>' expected: '<b>'.
Evolving a schema
The plugin has no separate compatibility-checking step (no forward/backward/full compatibility modes). Compatibility is a side effect of two mechanisms:
- Optional fields
-
A field typed as a two-branch union with
null(for example["null", "string"]) may be omitted from the test configuration; it resolves tonull. This is the supported way to add a field that old consumers/providers do not know about. - Schema defaults
-
A field omitted from the test configuration that has an Avro default value uses that default instead of failing configuration. Give new fields a default value if you want existing tests to keep working unmodified after the field is added.
Anything else — a required field with no default, or removing/renaming a field a test still references — surfaces as a configuration error at test-authoring time (see Troubleshooting & FAQ) rather than as a silent compatibility pass/fail. There is no support for wider unions (more than two branches, or two branches without null): these are always rejected, regardless of whether they represent a "compatible" schema change.
Practical guidance
-
Treat each
.avscfile as a pinned snapshot for the interactions built from it. If you need to test against multiple versions of a message concurrently (for example during a migration), keep them as separate schema files, as the example project does withorders.avscandorder-v1.avsc. -
When you widen a schema (add an optional or defaulted field), existing consumer tests do not need to change; new tests can start asserting on the new field.
-
When you narrow or rename a field that existing tests configure, those tests fail at Pact-file-generation time with the errors above, not at verification time. Fix the test configuration alongside the schema change.
Want to help? Learn how to contribute to the Compress4J docs ›