counterfeiter: Go fakes that stop compiling when the interface changes
A tool for generating self-contained, type-safe test doubles in go
At a glance
- What is it?
- counterfeiter generates test doubles for Go interfaces, driven by go generate directives so the fakes live next to the code they fake, and every generated fake ends with a compile-time assertion that it satisfies the interface, which means a stale fake breaks the build rather than passing silently. It needs Go 1.24 or later for the go tool directive.
- Who is it for?
- counterfeiter suits a Go team that keeps its interfaces small, owns them in the package that uses them, and wants fakes that are regenerated on every build rather than hand-maintained. It is the wrong choice if your interfaces are large or change often, since the generated fake grows with the interface and every change means a regeneration, and a poor fit for a codebase that cannot run go generate, since a stale fake is a compile error by design.
- 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 last received commits 9 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
The problem: Go cannot build a fake at runtime, so someone has to write one
The README opens with the design constraint that explains the whole tool. Go code declares what it needs from its dependencies as interfaces, usually small ones defined by the package that uses them, and testing that code means supplying fake implementations of those interfaces. Go has no way to build one at runtime, so they are written by hand or generated. That last clause is the space counterfeiter occupies. The alternative to generation is a developer writing a struct that implements every method of every interface the package touches, and keeping it in step by hand, which is the kind of chore that either gets skipped or gets done badly. The tool's job is to turn that chore into a directive in the source file and a compile-time check that the result is still valid.
The generated fake: setter methods, call counters and argument capture
The README shows the interface declaration and then the test that uses it, and the second block is the API you actually touch. The fake is constructed as a pointer to foofakes.FakeMySpecialInterface, and then you configure it with a Returns method, fake.DoThingsReturns(3, nil), which sets what the method will give back. The call itself, fake.DoThings("stuff", 5), returns a number and an error, and the assertion checks the number. Then the generated fake records what happened, so there is a call count method, fake.DoThingsCallCount(), and an argument capture method, fake.DoThingsArgsForCall(0), which returns the arguments of the nth call. That last pair is what makes the fake useful beyond stubbing a return value, because you can assert on what the code under test actually passed in rather than only on what it got back.
fake := &foofakes.FakeMySpecialInterface{}
fake.DoThingsReturns(3, nil)
num, err := fake.DoThings("stuff", 5)
Expect(num).To(Equal(3))
Expect(fake.DoThingsCallCount()).To(Equal(1))
str, _ := fake.DoThingsArgsForCall(0)
Expect(str).To(Equal("stuff"))Three steps: a tool dependency, a directive, go generate
The Getting Started section assumes go 1.24 or later, which added go tool, and points at an older README for earlier versions. Step one is to establish a tool dependency with go get -tool github.com/maxbrunsfeld/counterfeiter/v6. Step two is to add one go:generate directive per package that runs counterfeiter -generate, and one counterfeiter:generate directive per interface you want a fake for, which you can add right next to your interface definitions or not, in any .go file in the package. Step three is to run go generate, either in the directory with your directive or at the root of your module to generate for all packages. The output is a line per fake, Writing FakeMySpecialInterface to foofakes/fake_my_special_interface.go. Because the tool is a go tool dependency rather than a binary on your PATH, the version is pinned by your go.mod, which removes the class of bug where a developer's global install is a different version from CI's.
go get -tool github.com/maxbrunsfeld/counterfeiter/v6
go generate ./...Why -generate exists, and the difference it makes on a big package
The two directive forms look redundant and are not. If a package only has an interface or two, you can skip the -generate form and the counterfeiter:generate directives, and run counterfeiter directly from one go:generate line per interface. But -generate is much faster once there are several directives in a package, because it loads the package once and writes every fake from one process, where each go:generate line starts a new one. That is a real consideration, because go generate runs each directive as a separate process invocation. With a package declaring thirty interfaces, the one-line-per-interface form means thirty processes each loading the package, and the -generate form means one. The README also notes the division of labour clearly, that a package needs only one go:generate line with -generate no matter how many counterfeiter:generate directives it has, while the counterfeiter:generate directives are the ones you add many of.
The defaults, and the compile-time assertion that makes stale fakes loud
The Defaults table is the part to read before generating anything, because four things are decided for you. The fake type is FakeMySpecialInterface, the interface name prefixed with Fake, changeable with -fake-name or -fake-name-template. The file is fake_my_special_interface.go, the fake's name in snake case, changeable with -o. The directory is foofakes/, a sibling package named after the package that declares the interface, and the package is foofakes or whatever package already lives in the output directory, changeable with -o, -test, or a file declaring the package. Then there is the sentence that makes the whole scheme work. Every fake ends with a compile-time assertion that it satisfies the interface, so when the interface changes, the stale fake stops compiling until you run go generate again. A hand-written fake that no longer matches an interface would still compile, and would fail at runtime, or worse, would not fail at all.
Faking interfaces you do not own: the standard library and third-party modules
You can fake any interface your module can import, and the README gives three forms. Name the interface as package-path.interface, for example io.WriteCloser or github.com/redis/go-redis/v9.Pipeliner, or give a directory of the package and the interface name, for example ../otherpackage OtherInterface. A third-party module has to be a dependency first, you have to go get it so it appears in your go.mod, because counterfeiter loads the interface from the module cache the same way the compiler would. The two forms also differ in where the fake lands, and the README is precise about it. With the package-path form, the fake goes into the fakes package of the directory the directive is in, so in package foo the io.WriteCloser line writes foofakes/fake_write_closer.go. With the directory form, it goes into the fakes package beside that directory, so it writes ../otherpackage/otherpackagefakes/fake_other_interface.go. You can override either with -o.
The import cycle problem, and the shared settings pattern
One case needs its own treatment. By default the fake lives in a sibling package named after the package that declares the interface, and tests inside that package cannot import it, because the fakes package imports the package and that would be an import cycle. The fix is to point -o at the interface's own directory, so the directive becomes //counterfeiter:generate -o . . MySpecialInterface. The other pattern worth knowing is how to share settings. Flags given alongside -generate on the go:generate line are the defaults for every counterfeiter:generate directive in the package, and those flags are -o, -header, -q, -test and -fake-name-template, while a directive's own flags take precedence. So a package whose fakes should all live in one fake package, named after their interfaces, is one line of configuration rather than a repeated flag on every directive.
//go:generate go tool counterfeiter -generate -o fake -fake-name-template '{{.TargetName}}'
//counterfeiter:generate . MyRepository
//counterfeiter:generate . MyPresenterThe module, the licence and where the project stands
The module is github.com/maxbrunsfeld/counterfeiter/v6, with a major version suffix that means the import path itself carries the version, which is the Go convention for a tool with a breaking API. It requires go 1.26.0. The dependencies are few and notable: golang.org/x/tools, which is how the tool loads and parses the source of the interface it is faking, and golang.org/x/text, plus test-only dependencies on go-cmp, gomega and sclevine/spec. The repository layout has a generator/ directory, a command/ directory, an arguments/ directory, a fixtures/ directory, an integration/ directory, a docs/ directory, a scripts/ directory and a .devcontainer/, along with benchmark_test.go and concurrency_test.go at the top level, which tells you the tool is performance sensitive and has been checked for concurrent use. The licence is MIT, the last push was on 2026-10-15, the recent releases are v6.12.2, v6.13.0 and v6.14.0 in 2026, and the repository is not archived.
Editorial conclusion
counterfeiter suits a Go team that keeps its interfaces small, owns them in the package that uses them, and wants fakes that are regenerated on every build rather than hand-maintained. It is the wrong choice if your interfaces are large or change often, since the generated fake grows with the interface and every change means a regeneration, and a poor fit for a codebase that cannot run go generate, since a stale fake is a compile error by design. Before adopting it, read the Defaults table rather than assuming, because the fake package name, the file name and the output directory all have defaults you may want to override, and note that the tool requires Go 1.24 or later for the go tool directive, with an older README for earlier versions.
Frequently asked questions
What does counterfeiter generate in Go?
It generates test doubles for a given interface. Given an interface MySpecialInterface with a DoThings method, it writes foofakes/fake_my_special_interface.go containing a FakeMySpecialInterface type, and the tests can set return values with DoThingsReturns, check the call count with DoThingsCallCount and read the arguments of a specific call with DoThingsArgsForCall.
How do I set up counterfeiter in a Go project?
Add the tool dependency with go get -tool github.com/maxbrunsfeld/counterfeiter/v6, add one go:generate directive per package running counterfeiter -generate plus a counterfeiter:generate directive per interface, then run go generate. The steps assume Go 1.24 or later, which added go tool.
What happens when an interface changes and the fake is stale?
Every fake ends with a compile-time assertion that it satisfies the interface, so when the interface changes, the stale fake stops compiling until you run go generate again. That is the mechanism that makes generated fakes safer than hand-written ones.
Can counterfeiter fake interfaces from the standard library or other modules?
Yes. You can fake any interface your module can import, naming it as package-path.interface such as io.WriteCloser or github.com/redis/go-redis/v9.Pipeliner, or giving a directory and interface name. A third-party module has to be a dependency first, since the interface is loaded from the module cache the way the compiler loads it.
How do I use a fake in tests inside the interface's own package?
By default the fake lives in a sibling package that imports the interface's package, which would be an import cycle for in-package tests. Point the output at the interface's own directory with a directive like //counterfeiter:generate -o . . MySpecialInterface.
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/maxbrunsfeld-counterfeiter)Was this useful?