Library / SDK
pact-foundation/pact-jvm avatar
pact-foundation/pact-jvm

Pact-JVM: contract tests where the consumer writes the rules and the provider replays them

JVM version of Pact. Enables consumer driven contract testing, providing a mock service and DSL for the consumer project, and interaction playback and verification for the service provider project.

1,148 stars497 forksKotlinApache-2.0

At a glance

What is it?
Pact-JVM is the JVM implementation of the Pact consumer driven contract library, written in Kotlin, and it splits into a consumer side that provides a mock service and a DSL and a provider side that does interaction playback and verification. The 4.7 line targets JDK 17 and above and the V4 specification with plugins.
Who is it for?
Pact-JVM fits a team with several services that want to catch integration breaks in a fast unit test rather than in a staging environment, and that accepts the cost of maintaining a contract file per consumer and provider pair. It is the wrong choice for a single service with no collaborators, and it is the wrong choice if you cannot run the provider's verification in CI.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 7 days ago.
What is it written in?
Mainly Kotlin, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 21, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The consumer writes the contract, the provider has to keep it

Pact-JVM is the JVM implementation of the consumer driven contract library, and the reference implementation it borrows its explanation from is the Ruby one.

The definition is a pact between service consumers and providers, enabling consumer driven contract testing. Pact provides a DSL for service consumers to define the HTTP requests they will make to a service provider and the HTTP responses they expect back. These expectations are used in the consumer's specs to provide a mock service provider. The interactions are recorded, and played back in the service provider's specs to ensure the service provider actually does provide the response the consumer expects.

And the payoff sentence: this allows testing of both sides of an integration point using fast unit tests.

The direction of authorship is the whole idea, and it runs against the usual instinct. The consumer's test suite is where the contract is written, because the consumer is the only party that knows what it actually sends and what it actually needs back. The provider does not get to define the contract, it gets to be tested against it.

That inverts the usual integration test. Instead of both sides pointing at a shared environment and hoping the versions line up, each side tests alone. The consumer's test spins up a mock provider that answers exactly what the consumer expects, and the provider's test replays the recorded interactions and fails if the provider's real behaviour differs.

The reference to Martin Fowler's article on consumer driven contracts is in the quoted text, and the getting-started link is a 2016 blog post on the topic, which is a reasonable summary of the project's age and its maturity.

The artefact list is long because the ecosystem is wide

The project summary carries a note about the JARs produced by the project, and it says they have changed with 4.1.x to better align with Java 9 JPMS. The artefacts are now:

code
au.com.dius.pact:consumer
au.com.dius.pact.consumer:groovy
au.com.dius.pact.consumer:junit
au.com.dius.pact.consumer:junit5
au.com.dius.pact.consumer:kotlin
au.com.dius.pact.consumer:spock
au.com.dius.pact:pact-jvm-server
au.com.dius.pact:provider
au.com.dius.pact.provider:spring
au.com.dius.pact.provider:spring6
au.com.dius.pact.provider:spring7
au.com.dius.pact.provider:maven
au.com.dius.pact:provider
au.com.dius.pact.provider:junit
au.com.dius.pact.provider:junit5
au.com.dius.pact.provider:lein
au.com.dius.pact.provider:gradle
au.com.dius.pact.provider:junit5spring
au.com.dius.pact.core:support
au.com.dius.pact.core:model
au.com.dius.pact.core:matchers
au.com.dius.pact.core:pactbroker

Two things are worth noting in that list. The first is the split by role, where everything under `consumer` is the consumer-side DSL and mock service and everything under `provider` is the verification side, and the `core` artefacts are the shared model, matchers, support, and the Pact broker client. The `au.com.dius.pact.core:model` is the one the Maven Central badge points at, which tells you it is the artefact other projects depend on directly for the data model.

The second is the explosion of provider variants. There is a separate artefact for Spring, for spring6, for spring7, for Maven, for JUnit, for JUnit 5, for Leiningen, for Gradle, and a combined junit5spring one. Each of those is a different way of wiring a provider verification into a build, and supporting all of them from one codebase is what the JPMS-aligned reorganisation at 4.1.x was about.

The consumer side has the same shape, with a DSL for Groovy, JUnit 4, JUnit 5, Kotlin, and Spock. So the library is not a single dependency but a family of them, and choosing the wrong coordinate is an easy mistake.

Three supported lines, and a long tail of retired ones

The supported JDK and specification versions table is the operational contract for a library like this, and it is worth reading carefully because the support matrix is narrow and the history is long.

The currently supported branches are 4.7.x on the master branch, 4.6.x, and 4.5.x. All three use the V4 specification with plugins. The 4.7.x branch requires JDK 17 or higher, tested up to 23, uses Kotlin 2.3.20, and is at 4.7.0. The 4.6.x branch also requires JDK 17 or higher but is tested up to 18, uses Kotlin 1.8.22, and is at 4.6.20. The 4.5.x branch requires JDK 11+ or 17+, with a note that the Spring6 support library requires JDK 17 while the rest of the Pact-JVM 4.5.x libraries require 11+, and it uses Kotlin 1.7.20 at version 4.5.13.

That note is a good example of the kind of detail that bites. A library line that supports two JDK baselines has a split where one module in the line needs the newer one, and if your build resolves the Spring6 artefact and your CI runs on JDK 11, the failure appears in one artefact rather than across the board.

Below that there is a table of previous versions, explicitly not actively supported, running from 4.4.x down to 2.4.x, with a column for Scala versions. The Scala support is a good indicator of the project's history: 3.6.x and 3.5.x list Scala 2.12 and 2.11, and 2.4.x lists 2.10 and 2.11, while from 4.3.x upwards the Scala column is N/A. So this was a project that supported Scala for years and then moved to Kotlin as its implementation language, keeping the specification version and the API shape stable across the transition.

Two more notes attach to that older table. V4 specification support is only partially implemented with 4.2.x, and v4.2.x may run on JDK 16 but the build for it does not.

The release history is steady: 4.7.3 in June 2026, 4.7.4 in August 2026, and 4.7.5 in August 2026, with the last push to the repository on 2026-09-25.

Pick the DSL that matches your test framework, not your language

The service consumers section of the project summary is organised by language, and the advice in each case is to use a particular library rather than a particular module of the same one.

For Scala, the recommendation is pact4s, a separate project. For Java, it is the junit consumer library for JUnit 4 tests and the junit5 consumer library for JUnit 5 tests, plus, if you are using Java 11 or above, an updated DSL for consumer tests. There is a note that if you are using Java 8 there is no separate Java 8 support library anymore, so the earlier approach applies.

For Groovy or Grails, the section points at the Spock consumer library.

The organising idea is worth making explicit, because it is easy to misread. The consumer DSL is not one library with different syntaxes; there is a separate artefact per test framework, and each is the natural fit for that framework's idioms. A JUnit 5 project uses the JUnit 5 DSL, a Spock project uses the Spock DSL, and a Scala project uses a different project entirely.

This is the design choice that makes the library pleasant in the framework you already use. A contract test that reads like a normal test in your framework is one you will actually write, and the cost of the framework-specific artefact is a dependency you already have.

On the provider side, the artefact list shows the same principle applied to build tools and frameworks, with the Spring, Maven, Gradle, Leiningen, and JUnit variants. The provider side is where you point at a broker or a pact file and assert that the real service honours the recorded interactions, and each of those artefacts wires that assertion into a particular build.

The `pact-jvm-server/` directory at the root is the standalone server mode, and `pact-publish/` is the publishing step, while `compatibility-suite/`, `pact-specification-test/`, and `core/` are the parts of the project that check the implementation against the specification itself.

Where Pact-JVM is the wrong tool

The limits are about the shape of the problem rather than about the library's quality, and they are worth naming before a team invests in a contract testing programme.

First, contracts only help when there are contracts to write. Pact is for a service with a consumer and a provider, and the cost scales with the number of pairs. An organisation with twelve services in a call graph has far more consumer and provider pairs than services, and each pair produces a pact file, a consumer test, and a provider verification. For a service with no collaborators, or for a monolith, there is nothing to contract test and the tooling is pure overhead.

Second, the contract is a claim, not a proof. The provider verification replays recorded interactions and checks that the responses match, which catches a provider that changed its behaviour. It does not catch a provider that behaves correctly for the requests a consumer happened to make during the recording window and incorrectly for everything else. A pact built from a test that exercises three endpoints tells you about three endpoints. Coverage of the contract is coverage of the consumer's tests.

Third, and this is the practical friction, the consumer and the provider have to agree on the pact, and they are usually different teams. When the provider wants to change a response shape, the consumer's contract has to change with it, and that is a negotiation the library does not automate. The Pact broker in the core artefacts helps with distribution, but the coordination cost is organisational and remains.

Fourth, the version matrix is genuinely wide, and a multi-module JVM application can end up needing several of those artefacts at once. A project on JDK 17 in 2026 is on the 4.7 line, fine. A project with a legacy module still on JDK 11 is on 4.5.x with the Spring6 caveat, and a project with an old service that has not been upgraded is on 4.3.x with partially implemented V4 support. Three lines, three specification support levels, and the contract files have to be compatible across them.

Fifth, the older lines are explicitly not actively supported, and the table of previous versions goes back to 2.4.x. That is a long history for a library where a mismatch between consumer and provider versions can mean a contract file one side cannot parse.

Sixth, and this is the general caveat about contract testing: it is a test strategy, and it works best when it is one layer in a suite. It does not replace integration tests in an environment where the real service is running, and a team that treats green pacts as proof that the integration works has misunderstood what the tool proves.

Editorial conclusion

Pact-JVM fits a team with several services that want to catch integration breaks in a fast unit test rather than in a staging environment, and that accepts the cost of maintaining a contract file per consumer and provider pair. It is the wrong choice for a single service with no collaborators, and it is the wrong choice if you cannot run the provider's verification in CI. Check three things before you commit: that your JDK is 17 or newer for the 4.7 line, where the table shows 17+ tested up to 23, that the artefacts you need are on the list for your version, because the coordinates were reorganised at 4.1.x for Java 9 JPMS alignment, and that the version of the Pact specification you generate is one your provider side also supports.

Frequently asked questions

What does Pact-JVM do for a consumer and a provider?

It provides a DSL for service consumers to define the HTTP requests they make to a service provider and the responses they expect, which the consumer's specs use to provide a mock service provider. The interactions are recorded and played back in the service provider's specs, so both sides of an integration point can be tested with fast unit tests.

Which Pact-JVM version should I use and what does it need?

The 4.7.x branch on master requires JDK 17 or higher, tested up to 23, uses Kotlin 2.3.20, and implements the V4 specification with plugins. The 4.6.x branch also needs JDK 17 but is tested up to 18, and the 4.5.x branch needs JDK 11 or 17 depending on the module, since the Spring6 support library requires JDK 17 while the rest of the 4.5.x libraries require 11+.

Which Maven coordinates do I need for a Pact-JVM consumer test?

There is a separate consumer artefact per test framework, so `au.com.dius.pact.consumer:junit` for JUnit 4, `au.com.dius.pact.consumer:junit5` for JUnit 5, `au.com.dius.pact.consumer:kotlin`, `au.com.dius.pact.consumer:groovy`, and `au.com.dius.pact.consumer:spock`. The artefacts were reorganised with 4.1.x to align with Java 9 JPMS.

Does Pact-JVM still support Scala and Java 8?

The previous versions table lists Scala versions only for the 3.6.x and earlier lines, with N/A from 4.3.x upwards, and the consumer section notes that if you are using Java 8 there is no separate Java 8 support library anymore. For Scala, the project summary points to pact4s as a separate implementation.

Official sources

  1. License: Apache-2.0
  2. pact-foundation/pact-jvm on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/pact-foundation-pact-jvm.svg)](https://hysenlabs.com/projects/pact-foundation-pact-jvm)

Was this useful?