Framework
uber-go/mock avatar
uber-go/mock

uber-go/mock: a maintained fork of GoMock for Go interface mocking

GoMock is a mocking framework for the Go programming language.

3,414 stars179 forksGoApache-2.0

At a glance

What is it?
Uber forked Google's golang/mock and keeps it current. This covers how mockgen works in its three modes, how to install it, where it falls short, and how it compares to Mockery.
Who is it for?
Teams writing Go tests against interfaces should adopt uber-go/mock if they want a maintained fork of the original gomock with a familiar API and a mockgen tool that covers archive, source and package modes. Teams that prefer test doubles generated from hand-written stubs, or that want a tool with no code generation step at all, should look elsewhere.
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 35 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 September 27, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Why Uber forked gomock and who the fork is for

GoMock generates mock implementations of Go interfaces so tests can substitute a fake for a real dependency. Google originally maintained the project under golang/mock. The README states plainly that Google no longer maintains it, and that Uber, given heavy internal usage, decided to fork and maintain it going forward. That history is the whole pitch: the API is the one Go developers already know, and the maintenance moved.

The audience is narrow and specific. You need interfaces. If your code passes concrete structs around, mockgen has nothing to generate. The README describes integration with Go's built-in testing package while noting the framework can be used in other contexts. In practice this means a test file creates a controller, constructs a generated mock, declares expectations, and passes the mock into the code under test.

Two groups get the most out of it. Teams with large interfaces at service boundaries, where writing a hand-rolled stub for every method is tedious and drifts as the interface changes. And teams migrating from golang/mock, because the import path changes to go.uber.org/mock but the shape of the code does not. The sample/ directory in the repository, with its imp1 through imp4 packages and mock_user_test.go, is the reference for what a finished setup looks like.

How mockgen generates mocks: archive, source and package modes

The generator is a separate binary. It reads Go code, finds interfaces, and writes a Go file containing mock types plus a constructor per interface. The README describes three modes of operation, and the mode you pick is the main design decision.

Source mode takes a file. You pass -source=foo.go and mockgen parses that file directly. It is the mode the README recommends for simple cases. Its weakness is scope: embedded interfaces defined in another file are not visible unless you add -aux_files, which takes a comma-separated list of foo=bar/baz.go entries mapping a package name to a source file.

Package mode takes two positional arguments: an import path and a comma-separated list of symbols. mockgen database/sql/driver Conn,Driver is the README's example. Passing . refers to the current package, which is the convenient form for a //go:generate directive. This mode resolves interfaces the way the compiler sees them, so it handles cross-file embedding without the auxiliary file dance. It also accepts -build_flags, passed verbatim to go list.

Archive mode reads a compiled package archive. You build the package first, then point mockgen at the artifact with -archive. The README's example builds database/sql/driver into pkg.a and passes the import path and symbols alongside the flag. This is the least convenient mode for day-to-day work, but it is the one that does not depend on parsing source at all.

Output control is where the flags earn their keep. -destination writes to a file instead of standard output. -package sets the generated package name, defaulting to mock_ prefixed to the input package. -self_package exists to break import cycles when the mock package would otherwise import itself. -mock_names lets you override generated type names per interface, and -exclude_interfaces drops ones you do not want. -typed changes the generated Return, Do and DoAndReturn methods to type-safe variants, and it defaults to false, so existing code that relies on interface{} arguments keeps compiling.

Installing mockgen and writing a first expectation

Installation is one command. The README gives it as go install go.uber.org/mock/mockgen@latest. The binary lands in GOPATH/bin, so if the next command fails, the README's remedy is to add that directory to PATH.

bash
go install go.uber.org/mock/mockgen@latest
mockgen -version
export PATH=$PATH:$(go env GOPATH)/bin

If mockgen -version prints a version, the tool is on your path. If the shell reports command not found, the export line above is the fix.

Now generate a mock. The README's package-mode example uses database/sql/driver, but for a first run against your own code, source mode is the shortest path. The README's source-mode invocation is this:

bash
mockgen -source=foo.go [other options]

With -destination omitted, the generated source goes to standard output, which is useful for inspecting what mockgen produces before you commit it to the repository.

The generated mock is used through a controller. The README's building-mocks example creates one with gomock.NewController(t), constructs the mock, declares an expectation with EXPECT(), and passes the mock into the system under test:

go
func TestFoo(t *testing.T) {
  ctrl := gomock.NewController(t)
  m := NewMockFoo(ctrl)
  m.EXPECT().Bar(gomock.Eq(99)).Return(101)
  SUT(m)
}

The comment in the README is explicit about the semantics: the expectation asserts that the first and only call to Bar is passed 99, and anything else fails. That strictness is the point of the framework, and it is also what surprises newcomers, because an unexpected extra call is a test failure rather than a silent pass.

For a stub rather than an assertion, the README shows a different pattern: DoAndReturn with AnyTimes(). AnyTimes means the expectation does not assert on call count, and DoAndReturn executes a function instead of returning a fixed value. The README's stub example sleeps for a second inside that function to simulate latency. Mixing the two styles in one test file is normal, but it is worth being deliberate about which expectations assert and which merely permit.

Failure messages, matchers and the parts the README leaves thin

When a matcher fails, the output shows the received value against the expected one. The README's example prints Got: [3] and Want: is equal to 2, followed by a line naming the call site and the argument index. The Want text comes from the matcher's String() method, and the Got text comes from the object's String() method when one is available. Both are replaceable. gomock.WantFormatter wraps a matcher and a StringerFunc, so gomock.Eq(15) can report is equal to fifteen instead of is equal to 15. That is a real affordance, and it matters when a matcher's default rendering is unreadable for the type you are comparing.

The documentation is thinner elsewhere. The README lists the flags with one-line descriptions and stops. There is no worked example for -aux_files, none for -mock_names beyond the flag description, and none for -self_package, which is arguably the flag most likely to be needed and least likely to be understood from a single sentence. The sample/ directory carries the load that the prose does not.

A more consequential gap is that the README does not document how generated files should be regenerated in CI, whether they should be committed, or how to keep them in sync when an interface changes. The -write_generate_directive flag exists and defaults to false, which suggests the intended workflow is a //go:generate line in the source file, but the README never says so outright. Teams will have to decide this themselves, and the decision affects review noise more than it affects correctness.

Where uber-go/mock is the wrong tool

The first limitation is structural: mockgen mocks interfaces. If a dependency is a concrete type, or a function value, or a package-level function, there is no interface for the tool to read. Go developers who want to fake a function often end up introducing an interface purely to make it mockable, which is a design change driven by a test tool. That is sometimes the right call and sometimes a smell, but the tool does not help you decide.

The second is the strictness of expectations. The README's own example asserts that Bar is called exactly once with 99. In a test that exercises a retry path, or a cache that may or may not be consulted, that rigidity produces failures that describe the test's assumption rather than a real defect. The escape hatch is AnyTimes() and the stub pattern, but using it everywhere gives up the assertion value that justified adopting the framework.

The third is code generation as a build step. Every interface change requires regenerating mocks, and the generated files are large. A team without an automated regeneration step will see stale mocks produce confusing compile errors or, worse, tests that pass against an interface that no longer matches production. The README does not document a rollback or a versioning strategy for generated output.

Finally, this is a fork. The README is candid that Google stopped maintaining the original, which is the reason the fork exists. Anyone adopting it should understand that the maintenance commitment now rests with Uber and its contributors, and that the Go version support policy is tied to the official Go Release Policy: the two most recent releases. If your project is pinned to an older toolchain, that policy is a hard constraint, not a suggestion.

uber-go/mock compared with Mockery and hand-written stubs

Mockery is the comparison Go developers ask about most, and the difference is in the generation model rather than the assertion API. Mockery generates mocks from a configuration file that describes packages and interfaces, and its newer major versions moved toward that config-driven approach. mockgen instead takes its input on the command line: a source file, an import path with symbols, or an archive. The practical consequence is that mockgen fits naturally into a //go:generate line next to the interface it mocks, while a config-driven generator centralizes the list of what gets mocked in one place. If you want per-interface locality, mockgen's model is the better fit. If you want one file that describes every mock in the repository, mockgen makes you write shell or Makefile glue to get there.

Testify's mock package is a different shape entirely. It provides a Mock object you extend at runtime with On and Return calls, with no code generation step. The trade-off is that the compiler cannot tell you when an interface method changes, because nothing was generated from the interface. mockgen's generated types fail to compile when the interface moves, which is exactly the signal you want in a large codebase.

Hand-written stubs remain a legitimate alternative for small interfaces. A two-method interface does not need a generator, and a hand-written stub can encode domain-specific default behavior that no generator would infer. The README's stub example shows that gomock can express the same thing through DoAndReturn and AnyTimes, but at that point you are writing the behavior yourself anyway, with an extra dependency in the build.

Maintenance, licence and what upgrading involves

The repository is not archived, and the last push was on 2026-08-25. Recent releases listed are v0.6.0 in August 2025, and bazel_rule_v0.2.0 and bazel_rule_v0.1.0 in June 2025. The version numbering is worth noting: this is a v0 line, so the project does not promise the API stability that a v1 would imply, even though the surface API is inherited from a long-lived predecessor.

The go.mod declares go 1.25.0 and depends on golang.org/x/mod, golang.org/x/tools and github.com/stretchr/testify, with indirect dependencies on go-spew, go-difflib, goldmark, x/sync and yaml.v3. The direct dependency set is small, which keeps the supply chain shallow for a tool that runs in the build rather than in production. The Bazel rule releases are separate tags, so Bazel users track a different version line from the mockgen binary.

Licensing is Apache-2.0, the same licence the project carried before the fork. That permissiveness is why the fork was possible at all, and it means generated code carries no copyleft obligation. Including a copyright header in generated files is supported through -copyright_file, which is the flag to reach for if your organisation requires one. This is a description of what the repository states, not legal advice; teams with specific compliance requirements should read the LICENSE file themselves.

Upgrading is a matter of re-running the install command and regenerating. Because generated mocks are ordinary Go source, a mockgen upgrade can change the emitted code, so the diff after regeneration is the thing to review. The README does not document a migration path between mockgen versions, and there is no changelog excerpt beyond the release tags.

Editorial conclusion

Teams writing Go tests against interfaces should adopt uber-go/mock if they want a maintained fork of the original gomock with a familiar API and a mockgen tool that covers archive, source and package modes. Teams that prefer test doubles generated from hand-written stubs, or that want a tool with no code generation step at all, should look elsewhere. Before committing, verify that your Go toolchain matches the two most recent releases the project supports, check whether -typed output fits your codebase style, and confirm that the generated mocks land in a directory your build includes. The repository last received a push on 2026-08-25, so the fork is still moving.

Frequently asked questions

What are the differences between Mockery and uber-go/mock?

Mockery generates mocks from a configuration file describing packages and interfaces, while mockgen takes its input on the command line as a source file, an import path with symbols, or a compiled archive. The practical difference is locality: mockgen fits a //go:generate line beside the interface, and a config-driven tool centralizes the list.

How do I install mockgen for uber-go/mock?

The README gives the command go install go.uber.org/mock/mockgen@latest. Verify it with mockgen -version, and if that fails, add GOPATH/bin to PATH using export PATH=$PATH:$(go env GOPATH)/bin.

What are the three modes mockgen can run in?

Archive mode reads a compiled package archive with -archive, source mode parses a file with -source, and package mode takes an import path plus a comma-separated list of symbols as two positional arguments. The README recommends source mode for simple cases.

Why does uber-go/mock exist instead of golang/mock?

The README states that Google no longer maintains the original golang/mock repository, and that Uber forked and maintains it going forward given heavy internal usage. The import path changed to go.uber.org/mock.

Which Go versions does uber-go/mock support?

The README says it supports all Go versions covered by the official Go Release Policy, which is the two most recent releases. The go.mod file declares go 1.25.0.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. README
  4. Releases
  5. uber-go/mock on GitHub
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/uber-go-mock.svg)](https://hysenlabs.com/projects/uber-go-mock)