pact-go: contract testing for Go services, and the native library it drags along
Golang version of Pact. Pact is a contract testing framework for HTTP APIs and non-HTTP asynchronous messaging systems.
At a glance
- What is it?
- pact-go is the Go implementation of the Pact contract testing framework, where a consumer test writes the expectation and a provider test verifies it, but the setup cost is a cgo dependency on a Rust-built shared library.
- Who is it for?
- Adopt pact-go when several Go services call each other over HTTP or messaging and your end-to-end suites have become slow and brittle, since the consumer-driven contract replaces a shared test environment with a checked-in contract file. Do not adopt it for a single service with no consumers to pin, and think twice in any container build that forbids sudo, because the documented default install path writes to /usr/local/lib through a cgo dependency on a Rust FFI library.
- Can I use it commercially?
- Yes. MIT 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 received new commits within the last day.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Why contract tests replace the end-to-end suite
pact-go is the Go implementation of Pact, a contract testing framework described in the project documentation as the de-facto API contract testing tool, and the pitch is to replace expensive and brittle end-to-end integration tests with faster unit tests. The mechanism is asymmetric on purpose. The consumer writes a test that sets out its assumptions about the API it calls, and running that test produces a contract file that the provider then verifies against its own implementation. Both sides check the same expectations without needing a running copy of the other side, which is what removes the need for complex test environments and large data fixtures. It applies to HTTP and REST as well as event-driven systems, and the framework is described as supporting more than a dozen languages, so a Go consumer can pin a service written in something else.
One consumer test, one mock server, one contract
The consumer entry point is the package `github.com/pact-foundation/pact-go/v2/consumer`, and the worked example in the documentation is a User API client with a single `GetUser(id)` method returning a `*User`. Pact tests follow the Arrange, Act, Assert pattern, and the arrange phase is where the expectation is declared. The example reads like this:
func TestUserAPIClient(t *testing.T) {
// Specify the two applications in the integration we are testing
// NOTE: this can usually be extracted out of the individual test for re-use)
mockProvider, err := NewV2Pact(MockHTTPProviderConfig{
Consumer: "UserAPIConsumer",
Provider: "UserAPI",
})
assert.NoError(t, err)The interaction builder chains `AddInteraction()` into `Given`, `UponReceiving`, `WithRequest("GET", S("/user/10"))`, `WillRespondWith(200)` and `WithBodyMatch(&User{})`, and the act phase then calls `ExecuteTest` with a callback that runs the real client against the mock server. The point of the matching rules attached to the body is that the consumer asserts the shape it needs, not an exact byte-for-byte payload, which is what keeps the test from breaking on an unrelated field change.
Getting the CLI and the native libraries in place
Installation has two halves, and the library half is the one people underestimate. The Go side is conventional:
go get github.com/pact-foundation/pact-go/v2
go install github.com/pact-foundation/pact-go/v2
pact-go -l DEBUG installThe `pact-go` binary lands in `$GOPATH/bin`, which is `$HOME/go/bin` by default, so if the command is not found the path needs adjusting:
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/binThe third command is the one that fetches the native side, the shared libraries the tests load at runtime. Two escape hatches exist for version drift and odd layouts. Calling `version.CheckVersion()` keeps the library versions in step with the Go module, and setting `PACT_GO_LIB_DOWNLOAD_PATH` tells the tool where the library lives when it is not in the default location.
cgo, gcc and a directory that needs sudo
This is the sharpest edge in the project. Pact relies on C shared libraries compiled from Rust, so `cgo` is a prerequisite and `CGO_ENABLED=1` must be set, which the documentation tells you to check with `go env`. Platform setup then differs. Linux needs the `gcc` package, macOS needs the Xcode command line tools, and Windows needs `gcc` installed through `choco install mingw` or `scoop install mingw`, after which the library directory has to be added to `PATH` and `CGO_LDFLAGS`, with separate snippets given for PowerShell and Command Prompt. The default install target is `/usr/local/lib`, which is not user-writable, so the documentation is explicit that `pact-go install` must be run with `sudo`. That single requirement is what makes this hard to run in a locked-down CI image, and the escape hatch is to install elsewhere with `pact-go -l DEBUG install --libDir /tmp`. A further alternative is to copy the shared library into a writable directory and point `CGO_LDFLAGS` at it.
Manual installation of the FFI library
For environments where the CLI installer cannot run, the documentation describes downloading the latest `Pact FFI Library` for your operating system and placing it on a standard library search path, with `/usr/local/lib` suggested for macOS and Linux. The file extension depends on the platform: `.dylib` on macOS, where M1 users need the `aarch64` build, called `aarch64-apple-darwin` in versions before v0.4.21, `.so` on Linux and `.dll` on Windows. The macOS sequence looks like this:
wget https://github.com/pact-foundation/pact-reference/releases/download/libpact_ffi-v0.4.21/libpact_ffi-macos-x86_64.dylib.gz
gunzip libpact_ffi-macos-x86_64.dylib.gz
mv libpact_ffi-macos-x86_64.dylib /usr/local/lib/libpact_ffi.dylibThe check that the installation worked is a single command:
pact-go helpIf that prints help, the shared library is loadable. Note that the download URL is pinned to a specific release of the separate `pact-reference` repository, so this path needs updating by hand when versions move.
HTTP contracts and message contracts in one framework
Pact is not only an HTTP tool, and the repository layout shows the breadth. The top-level packages include `consumer`, `provider`, `message`, `matchers`, `proxy`, `command`, `installer`, `models`, `log`, `utils` and `version`, which separates the consumer side, the provider verification side, asynchronous message contracts and the matcher machinery that keeps assertions loose enough to survive refactors. The example directory backs that up with `avro`, `grpc`, `protobuf-message`, `plugin` and `types` alongside separate `consumer_v2_test.go`, `consumer_v3_test.go` and `consumer_v4_test.go` files, so several generations of the consumer API exist side by side. The documentation splits the guides the same way, with `docs/consumer.md`, `docs/provider.md`, `docs/messages.md`, `docs/migration.md` and `docs/troubleshooting.md`. Integration with Pact Broker or Pactflow is what turns these files into CI and CD workflows where a change that breaks a contract fails a build.
Version pinning and the cost of upgrading
The Go module path carries the major version, `github.com/pact-foundation/pact-go/v2`, and `go.mod` declares Go 1.25.0 with direct dependencies on cobra, testify, goavro, grpc, protobuf and yaml. Recent tags include v2.8.0 on 2026-09-16, v2.7.1 on 2026-08-26 and v2.7.0 on 2026-08-07, and the last push was on 2026-10-18, so the library is moving on a normal release cadence. Two upgrade costs sit on the Go side and one on the native side. On the Go side, the presence of separate v2, v3 and v4 consumer test files means consumer API versions coexist, so a migration has to be planned rather than absorbed, and `docs/migration.md` exists for that. On the native side, the FFI library version has to track the Go library, which is what `version.CheckVersion()` is there to police. The licence is MIT, so embedding the library in an internal test harness carries no copyleft obligation.
Editorial conclusion
Adopt pact-go when several Go services call each other over HTTP or messaging and your end-to-end suites have become slow and brittle, since the consumer-driven contract replaces a shared test environment with a checked-in contract file. Do not adopt it for a single service with no consumers to pin, and think twice in any container build that forbids sudo, because the documented default install path writes to /usr/local/lib through a cgo dependency on a Rust FFI library. Verify first that `pact-go -l DEBUG install` succeeds inside your CI image and that the shared library lands where `CGO_LDFLAGS` and `PATH` can find it.
Frequently asked questions
What does pact-go need installed before any test runs?
The `pact-go` CLI and the native Pact FFI shared library, plus `cgo` with `CGO_ENABLED=1` set. On Linux that means the `gcc` package, on macOS the Xcode command line tools, and on Windows `gcc` installed through `choco install mingw` or `scoop install mingw` with the library directory added to `PATH` and `CGO_LDFLAGS`.
Why does pact-go install need sudo on macOS and Linux?
By default `pact-go install` writes to `/usr/local/lib`, which is not user-writable. The alternative is `pact-go -l DEBUG install --libDir /tmp`, or installing the shared library manually and pointing `CGO_LDFLAGS` at it.
Can pact-go test asynchronous messaging as well as HTTP?
Yes. The project describes Pact as a contract testing framework for HTTP APIs and non-HTTP asynchronous messaging systems, with a dedicated `message` package, a `docs/messages.md` guide and examples covering Avro, gRPC and protobuf messages.
How do the consumer and provider sides of a pact-go contract meet?
The consumer test generates the contract, and the provider verifies it against its own implementation. The framework documents integration with Pact Broker or Pactflow for CI and CD workflows, so a breaking change fails a build instead of surfacing later in a combined environment.
Official sources
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.
[](https://hysenlabs.com/projects/pact-foundation-pact-go)Was this useful?