go-cmp: comparing Go values in tests without the reflection surprises
Package for comparing Go values in tests
At a glance
- What is it?
- go-cmp replaces reflect.DeepEqual in Go tests with an equality function that respects Equal methods, supports tolerance for floats, and panics on unexported fields instead of silently skipping them.
- Who is it for?
- go-cmp belongs in any Go project whose tests compare structs rather than scalars, because it fails loudly where `reflect.DeepEqual` fails quietly. The panic on unexported fields is the feature that matters most: it turns a comparison that was never really checking your state into an error you have to answer.
- Can I use it commercially?
- Yes. BSD-3-Clause 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 127 days ago.
- 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
Adding the package to a Go module
There is one install step, and it is a module command rather than a build:
go get -u github.com/google/go-cmp/cmpThe package path ends in `/cmp`, not at the repository root, which trips people up the first time. The module is declared in `go.mod` as `github.com/google/go-cmp` and requires Go 1.21, so anything on an older toolchain needs a version bump before this is an option. The `cmp` subdirectory in the repository tree is where the library lives; the rest of the top level is `LICENSE`, `CONTRIBUTING.md`, a `.gitattributes` file and CI configuration.
Because it is a normal module dependency rather than a code generator or a code generator's output, adding it changes your `go.mod` and `go.sum` and nothing else. There is no binary to install and no initialization step. That is the entire setup, and the README stops there.
The project describes itself as not an official Google product, and the license is BSD 3-Clause. Both matter more than they sound: the BSD license is permissive, with the no-endorsement clause that most Go libraries ship under.
Three ways equality is decided
The README names three mechanisms, and the order they apply in is the part worth understanding.
The first is custom equality functions supplied by the caller. These override the comparison for a particular type or a particular path, and the README's example is the canonical case: reporting floats as equal when they fall within a tolerance. This is the feature people adopt go-cmp for, because floating point equality in tests is otherwise a recurring source of flakes.
The second is the `Equal` method. If a type defines one, go-cmp uses it. This lets the author of a type decide what equality means for it, rather than leaving the decision to every test that touches the value. A time type, a decimal type or a domain object with an identity concept can all express that directly.
The third is the fallback: recursively comparing primitive kinds, much like `reflect.DeepEqual` does. This is where go-cmp deliberately diverges from the standard library, and the divergence is the source of its most valuable behavior. If neither a custom function nor an `Equal` method applies, go-cmp recurses into the value and compares kinds. For most data that behaves the same as the standard library call it replaces, which is the point: the migration is mostly a change of function name.
Why unexported fields cause a panic
Read the README's description of the fallback carefully. Unlike `reflect.DeepEqual`, unexported fields are not compared by default, and they result in panics unless you suppress them with an `Ignore` option such as `cmpopts.IgnoreUnexported`, or compare them explicitly with `AllowUnexported`.
That is a deliberate inversion of the standard library's behavior. `reflect.DeepEqual` cannot read unexported fields through reflection, so it skips them and reports two values as equal even when their hidden state differs. A test comparing two objects that both hold an unexported mutex, cache or closure will pass under `reflect.DeepEqual` whether or not that state matches. go-cmp makes that same situation a panic instead, because a test that cannot check what it claims to check is worse than one that fails to build.
The escape hatches are both explicit and both name the type involved. `cmpopts.IgnoreUnexported` accepts the types whose internals you have decided do not matter, which is the right choice for a sync.Mutex you never want compared or a func field that is not comparable at all. `AllowUnexported` does the opposite, opting a type into real comparison of its private fields, and requires the calling package to be able to construct the value it needs. Neither is the default, and both make the exception visible in the test source.
Reading the release notes for the option set
The releases are short and mostly about API surface, which is a good sign for a library this small. `v0.7.0`, published 2025-02-21, added support for compare functions with `SortSlices` and `SortMaps`, and improved the panic message when a `proto.Message` field could not be exported.
That SortSlices and SortMaps point is the one with practical value. Slice order and map iteration order are not part of the value in most designs, but the default comparison cares about them, so comparing two slices built in a different order fails. With v0.7.0 you can attach a comparer to a slice or map comparator and say that order does not matter, instead of sorting both sides by hand in every test.
The second message improvement matters more than it sounds. When go-cmp panics on a type it cannot inspect, knowing that the culprit was a protobuf message rather than some arbitrary struct is the difference between a two-minute fix and a search. The project is clearly responding to real test-failure reports here.
`v0.6.0` from 2023-10-10 added `cmpopts.EquateComparable` and removed purego fallbacks. `v0.5.9` from 2022-09-08 was cleanup: adjusting the heuristic for line-based versus byte-based diffing, using `reflect.Value.IsZero`, and formatting with the Go 1.19 formatter. None of those are feature releases, and a package whose last three releases are an option, a build change and a cleanup is not going to surprise you with a behavioural rewrite. The last push was on 2026-06-18.
Where cmpopts carries the load
The repository tree is minimal, with `cmp/` holding the library and `tests/` the test suite. There is no `examples/` directory and no separate documentation tree, so everything beyond the README lives in the package documentation on pkg.go.dev. That is where `cmpopts` is specified, and it is worth planning around: the README's role is to explain the three equality mechanisms and point you at the docs, not to be a reference you can implement from.
What lives in `cmpopts` is the practical toolbox. `IgnoreUnexported` and `AllowUnexported` handle the private field problem described earlier. `EquateComparable` lets a type be compared by value even when it does not implement the comparable constraint in the way the default comparison expects, which matters for generics-era code. The sort-aware comparers from v0.7.0 are the newest addition and the most likely thing you have not seen in other articles about this package.
The design constraint behind all of this is that a comparator has to be given a path to the field it applies to, so the option you want for one field does not leak into an unrelated field of the same type elsewhere in the structure. Options are values passed into the call rather than global configuration, which means a test states its own rules explicitly and another test in the same package is unaffected. That is the right shape for a testing library, since tests in one file should not be able to change the meaning of a comparison in another.
What go-cmp is not, and where it stops
It is worth being clear that go-cmp is a comparison library, not an assertion or diffing framework. It returns a boolean and produces a panic when it cannot do what you asked. It does not format a readable diff for you, it does not give you a table-driven test runner, and it does not integrate with your mocking library. The line-based versus byte-based diffing change in v0.5.9 belongs to the reporter inside the package, used when it prints a failure message, and is not an API you call.
The other boundary is the panic on unexported fields, and it will hit you sooner than you expect. Any type from the standard library or a third party with private state can trip it, including types you did not write and cannot change. Working around it means enumerating those types with `cmpopts.IgnoreUnexported`, which grows over time and is worth keeping in one shared test helper rather than repeating. A project with heavy third-party types in its compared structs will spend more time on this than on the tolerance features that attracted it.
Compared with the standard library, the argument is straightforward: `reflect.DeepEqual` silently under-compares, go-cmp refuses to. Compared with a mocking or snapshot-testing library, the argument is that they solve a different problem, since go-cmp answers one narrow question and leaves the test structure to you. If your tests already compare plain maps and slices of exported fields, go-cmp will feel like more ceremony than you need. If they compare domain objects with real identity semantics, the `Equal` method convention alone justifies it.
Editorial conclusion
go-cmp belongs in any Go project whose tests compare structs rather than scalars, because it fails loudly where `reflect.DeepEqual` fails quietly. The panic on unexported fields is the feature that matters most: it turns a comparison that was never really checking your state into an error you have to answer. Start with a plain `cmp.Equal` call, add a `cmpopts.IgnoreUnexported` only for types whose internals genuinely do not matter, and reach for a comparer only when a field has tolerance semantics like a float. Its surface is small enough to learn in one sitting, and the package documentation on pkg.go.dev covers everything the README leaves out.
Frequently asked questions
What is go-cmp used for in Go?
It is a package for comparing whether two Go values are semantically equal in tests, positioned as a more powerful and safer alternative to `reflect.DeepEqual`. It lets callers supply custom equality functions, respects a type's own `Equal` method, and otherwise compares primitive kinds recursively.
How do I install go-cmp?
Run `go get -u github.com/google/go-cmp/cmp`. Note that the import path ends in `/cmp` rather than at the repository root, and the module requires Go 1.21 or later.
How do I compare values with unexported fields?
By default go-cmp panics on unexported fields instead of skipping them. Use `cmpopts.IgnoreUnexported` for types whose internal state you have decided is irrelevant, or `AllowUnexported` to opt a type into real comparison of its private fields.
How do I compare floats with a tolerance in go-cmp?
Supply a custom equality function through the package's comparer options, which overrides the default comparison for that field. The README gives reporting floats as equal within a tolerance as its example of why custom equality functions exist.
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/google-go-cmp)Was this useful?