From 13e110f86e70205357697e9b87ba9494908b0752 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 23:33:00 +0000 Subject: [PATCH 1/2] :seedling: Bump github.com/maxbrunsfeld/counterfeiter/v6 Bumps [github.com/maxbrunsfeld/counterfeiter/v6](https://github.com/maxbrunsfeld/counterfeiter) from 6.12.2 to 6.13.0. - [Release notes](https://github.com/maxbrunsfeld/counterfeiter/releases) - [Commits](https://github.com/maxbrunsfeld/counterfeiter/compare/v6.12.2...v6.13.0) --- updated-dependencies: - dependency-name: github.com/maxbrunsfeld/counterfeiter/v6 dependency-version: 6.13.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 4 +- go.sum | 8 +- .../maxbrunsfeld/counterfeiter/v6/CLAUDE.md | 99 ++++ .../maxbrunsfeld/counterfeiter/v6/README.md | 482 ++++++++++++++---- .../counterfeiter/v6/arguments/parser.go | 180 ++++++- .../counterfeiter/v6/arguments/usage.go | 110 ++-- .../counterfeiter/v6/generator/cache.go | 13 +- .../counterfeiter/v6/generator/ctx.go | 2 - .../counterfeiter/v6/generator/ctx_old.go | 10 - .../counterfeiter/v6/generator/fake.go | 105 ++-- .../counterfeiter/v6/generator/file_reader.go | 6 + .../v6/generator/function_loader.go | 6 +- .../v6/generator/function_template.go | 10 +- .../v6/generator/interface_loader.go | 25 +- .../v6/generator/interface_template.go | 35 +- .../counterfeiter/v6/generator/loader.go | 308 +++++++++-- .../maxbrunsfeld/counterfeiter/v6/main.go | 43 +- .../x/tools/go/ast/astutil/imports.go | 10 +- .../x/tools/go/gcexportdata/gcexportdata.go | 103 ++-- .../x/tools/internal/gcimporter/iexport.go | 22 +- .../x/tools/internal/gcimporter/iimport.go | 14 +- .../x/tools/internal/gcimporter/ureader.go | 53 +- .../x/tools/internal/imports/fix.go | 33 +- .../x/tools/internal/imports/sortimports.go | 10 +- .../x/tools/internal/modindex/index.go | 48 +- .../x/tools/internal/pkgbits/version.go | 9 + .../x/tools/internal/stdlib/deps.go | 6 +- .../x/tools/internal/typesinternal/types.go | 2 + vendor/modules.txt | 8 +- 29 files changed, 1317 insertions(+), 447 deletions(-) create mode 100644 vendor/github.com/maxbrunsfeld/counterfeiter/v6/CLAUDE.md delete mode 100644 vendor/github.com/maxbrunsfeld/counterfeiter/v6/generator/ctx_old.go diff --git a/go.mod b/go.mod index bce0d56051..9121d6e2ad 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/go-viper/mapstructure/v2 v2.5.0 github.com/google/go-cmp v0.7.0 github.com/itchyny/gojq v0.12.19 - github.com/maxbrunsfeld/counterfeiter/v6 v6.12.2 + github.com/maxbrunsfeld/counterfeiter/v6 v6.13.0 github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/onsi/ginkgo/v2 v2.32.1 github.com/onsi/gomega v1.43.0 @@ -181,7 +181,7 @@ require ( golang.org/x/sys v0.48.0 // indirect golang.org/x/term v0.46.0 // indirect golang.org/x/text v0.42.0 // indirect - golang.org/x/tools v0.49.0 // indirect + golang.org/x/tools v0.50.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect diff --git a/go.sum b/go.sum index 906de9deb5..af5a6ee907 100644 --- a/go.sum +++ b/go.sum @@ -290,8 +290,8 @@ github.com/mattn/go-runewidth v0.0.27 h1:Feg/Oou5zI/wnpgDF6omIU0OokC9GxLC/WRknhV github.com/mattn/go-runewidth v0.0.27/go.mod h1:3qAiGCV4Koz/yuveO58qUefmUTRm8r0IGEXZ9jeHp/8= github.com/mattn/go-sqlite3 v1.14.49 h1:B8jBHC3xhxZgxztrgruTuLucebnULQnx4W7cF7SAE9w= github.com/mattn/go-sqlite3 v1.14.49/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w= -github.com/maxbrunsfeld/counterfeiter/v6 v6.12.2 h1:V23nK2R2B63g2GhygF9zVGpnigmhvoZoH8d0hrZwMGY= -github.com/maxbrunsfeld/counterfeiter/v6 v6.12.2/go.mod h1:Mr897yU9FmyKaQDPtRlVKibrjz40XXyOHUfyZBPSyZU= +github.com/maxbrunsfeld/counterfeiter/v6 v6.13.0 h1:ybB78iZ/Hkh76F6t+MbN+DP9WFNkLX+tB5JWOctzPyY= +github.com/maxbrunsfeld/counterfeiter/v6 v6.13.0/go.mod h1:pLuSACD770jzP/sacZMoGDxz34d5uiUpo+xdj/pYF/Y= github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= github.com/miekg/pkcs11 v1.1.2 h1:/VxmeAX5qU6Q3EwafypogwWbYryHFmF2RpkJmw3m4MQ= @@ -579,8 +579,8 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI= -golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo= +golang.org/x/tools v0.50.0 h1:c2ifzfcuY7L90lZ2aKd8S4K2NpASF08SZx9ZuJkHmSU= +golang.org/x/tools v0.50.0/go.mod h1:7ulVMw3831Mwi5EZD6RomGyffr4VFjuNYXf2BbCEAV0= golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= diff --git a/vendor/github.com/maxbrunsfeld/counterfeiter/v6/CLAUDE.md b/vendor/github.com/maxbrunsfeld/counterfeiter/v6/CLAUDE.md new file mode 100644 index 0000000000..4594230e21 --- /dev/null +++ b/vendor/github.com/maxbrunsfeld/counterfeiter/v6/CLAUDE.md @@ -0,0 +1,99 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What this is + +`counterfeiter` is a CLI (module `github.com/maxbrunsfeld/counterfeiter/v6`) that generates Go test doubles ("fakes") for interfaces, function types, and whole packages. It is typically invoked via `//go:generate` directives. Requires Go modules; CI runs on `stable` and `oldstable` Go on Linux and Windows. + +## Commands + +Full CI pipeline (vet → regenerate fakes → verify clean git tree → tests): + +```shell +./scripts/ci.sh # Linux/macOS +.\scripts\ci.ps1 # Windows +``` + +Individual steps: + +```shell +go vet ./... +go generate ./... # regenerate all fakes under fixtures/ (directives use `go run`, so no install needed) +./scripts/checkclean.sh # fail if regenerated fakes differ from committed ones +./scripts/cleanfakes.sh # delete every */*fakes/fake*.go (then `go generate ./...` to rebuild) +go test -race . ./fixtures/... # packages that exercise fakes or the generator concurrently +go test ./arguments/ ./command/ ./generator/ ./integration/ +``` + +Run a single package's tests or a single spec. Tests use `sclevine/spec` + `gomega`; spec names are nested, so match with a regex on the top-level test function and the spec path: + +```shell +go test ./generator/ -run TestGenerator +go test ./integration/ -run 'TestIntegration/round_trip_as_module/working_with_a_module' +go test ./arguments/ -run TestParsingArguments -v +go test ./command/ -run TestRunner +go test -run TestFakes . # generated_fakes_test.go at repo root +go test -race -run TestConcurrency . # concurrency_test.go at repo root; only meaningful with -race +go test -bench . -benchmem . # benchmark_test.go at repo root +``` + +Debug env vars: `COUNTERFEITER_DEBUG=1` enables log output; `COUNTERFEITER_DISABLECACHE=1` bypasses the package-load cache; `COUNTERFEITER_PROFILE=1` writes `counterfeiter.profile`; `COUNTERFEITER_NO_GENERATE_WARNING=1` silences the "use -generate" warning. In tests, `log.SetOutput(io.Discard)` is set in the top-level test functions — comment it out to see generator logs. + +## Architecture + +Pipeline for one run (`main.go` is intentionally thin and should stay that way): + +1. **`command.Detect`** (`command/runner.go`) turns the process into a list of `Invocation`s. In normal mode that is the single CLI invocation (it reads `GOFILE`/`GOLINE` from `go generate`). In `-generate` mode it scans every `.go` file in the cwd package for lines starting with `//counterfeiter:generate ` and builds one invocation per line. This is why `-generate` is much faster than many `//go:generate` lines: one process, one package load. +2. **`arguments.New`** (`arguments/parser.go`) parses each invocation's flags and positional args into `ParsedArguments`: source package dir, package path, interface name, fake name (`Fake` + exported interface name), output path (default `/fakes/fake_.go`), destination package name, and modes (`-p` package mode, `-` print to stdout, `-q`, `-header`). A `-header` on the top-level `-generate` line is inherited by directives that lack one (handled in `main.go`). +3. **`generator.NewFake`** (`generator/fake.go`) loads packages with `golang.org/x/tools/go/packages` (`loader.go`), finds the target `types.TypeName` (`findPackage`), and populates the `Fake` struct: `Methods` (from `interface_loader.go` / `package_loader.go`) or a single `Function` (`function_loader.go`), `Params`/`Returns` (`param.go`, `return.go`), and `Imports`. +4. **`Fake.Generate`** executes one of three `text/template`s — `interface_template.go`, `function_template.go`, `package_template.go` — then runs `goimports` (`imports.Process`) on the output. `main.go` runs `go/format` again and writes the file. + +Key supporting pieces: + +- **`generator.Imports`** (`import.go`) dedupes imports by package path and guarantees unique aliases (appends `a`, `b`, … on collision). `addImportsFor` in `loader.go` walks `types.Type` recursively to collect every package a fake needs; add a case there when a new `types.Type` kind shows up (it logs `!!! WARNING: Missing case`). +- **Generics**: `findPackage` / `getGenericTypeData` (`loader.go`) extract type params/constraints into `GenericTypeParameters*` strings used by the template. The compile-time assertion for a generic fake is emitted inside a blank generic func (`func _[T C]() { var _ pkg.I[T] = new(FakeI[T]) }`) so any constraint kind works. A target that is itself a constraint interface (unions or `~T`) is rejected up front because it cannot be implemented. +- **Package loading** (`loadPackages` in `loader.go`): the target package is the root of a single `packages.Load` call with `NeedName | NeedFiles | NeedImports | NeedTypes | NeedSyntax`, deliberately without `NeedDeps` or `NeedTypesInfo`. go/packages then type-checks only the target package from source and reads everything it imports from export data via `go list -export` (built through the normal go build cache), instead of type-checking the whole dependency graph on every run. `NeedSyntax` is what forces the root itself to come from source: export data omits unexported types that nothing exported refers to, which would break faking an unexported interface. When the target does not compile, `go list -export` attaches the compiler transcript as an unpositioned `ListError`, which `loadPackages` skips (`isBuildTranscript`) in favor of the positioned type errors. Nothing in the generator reads `TypesInfo`; do not add it back. Of the target package's own errors only `ParseError` is fatal; the rest (unresolved imports, type errors in other files) are kept in `Fake.loadErrors` and generation proceeds. It fails later, quoting them, only if the target's signatures mention an unresolved type (`hasInvalidType`, which does not look inside named types because they print by name) or the interface embeds one (`hasInvalidEmbed`, since the type checker silently drops such an embed from the method set). +- **Same-package fakes**: `main.go` passes the output directory via `generator.WithDestinationDir`. When it equals the target package's directory (`packages.Package.Dir`, symlinks resolved), `findPackage` sets `inTargetPackage`: the target package is not imported, its names print unqualified (the `types.Qualifier` returns `""` for packages absent from `Imports`), and the assertion is emitted even for unexported targets. A stale same-package fake never blocks regeneration because its type errors are tolerated like any other (see package loading). Extend `NewFake` only through `...Option`; its existing parameters are public API. +- **`Cacher`** (`cache.go`): `Cache` memoizes `packages.Load` results per package path across invocations in a `-generate` run; `FakeCache` is the no-op used by tests and `COUNTERFEITER_DISABLECACHE`. +- **`ctx.go`** holds `getBuildContext`, which returns `build.Default` with `build.Context.Dir` set to the working directory. + +## Testing conventions + +- `fixtures/` is a large corpus of interfaces exercising edge cases (aliases, dot imports, variadics, embedded interfaces, generics, hyphenated packages, package mode, vendored-style external packages, etc.). Each has `//go:generate` or `//counterfeiter:generate` directives; the generated fakes live in sibling `*fakes/` dirs and **are committed**. CI fails if `go generate ./...` produces a diff, so after changing templates or the generator, run `go generate ./...` and commit the regenerated fakes. +- `generated_fakes_test.go` (repo root) uses the committed fixture fakes as a behavioral test of the fake API (`Stub`, `CallCount`, `ArgsForCall`, `Returns`, `ReturnsOnCall`, `Invocations`). +- `concurrency_test.go` (repo root) drives `generator.NewFake` / `Generate` and `CachedFileReader` from several goroutines through shared caches. It only proves anything under `-race`, which is why it lives in the root package: CI runs `-race` there and on `fixtures/...` only, since `generator`, `integration`, `arguments`, and `command` are single-threaded and the race detector triples their run time. Anything concurrent added to the tool needs coverage here, not in its own package. +- `generator/generator_internals_test.go` unit-tests `NewFake`/`Generate` against fixtures directly. +- `integration/roundtrip_test.go` copies fixtures into a temp module, generates fakes, and runs `go build` on the result; some cases compare byte-for-byte against `integration/testdata/expected_*.txt`. Set `writeToTestData = true` in that file to dump actual output to `integration/testdata/output/` (gitignored) when debugging a mismatch. +- `.golangci.yaml` skips `fixtures/` from linting. + +## Package layout + +Keep the existing layout: `main.go` at the module root and the three subpackages `arguments`, `command`, and `generator`. `main.go` must stay at the root because users invoke the tool by module path (`go run github.com/maxbrunsfeld/counterfeiter/v6`, `go get -tool ...`); do not move it under `cmd/`. Do not restructure packages toward a different layout (for example Ben Johnson's Standard Package Layout, which the maintainer likes in general but has chosen not to apply here). Put new code in whichever of the three existing packages owns that pipeline stage. + +## Development workflow: red → green → refactor + +Every fix or feature follows strict TDD: + +1. **Red** — write a spec (`sclevine/spec` + `gomega`, matching the surrounding test file) that reproduces the issue and fails. For generator behavior this usually means adding a fixture interface under `fixtures/` plus an assertion in `generator/generator_internals_test.go` or `integration/roundtrip_test.go`. Run it and confirm it fails for the expected reason. +2. **Green** — write the simplest code that makes the spec pass. Nothing more. +3. **Refactor** — with the spec passing, look for duplication or simplification opportunities in the code just touched, keeping the suite green. Then regenerate fakes (`go generate ./...`) and run `./scripts/ci.sh`. + +Do not skip step 1, even for "obvious" one-line fixes. + +## Commits and pull requests + +Every commit carries a DCO sign-off from the person making it: always `git commit -s`. + +Write commit messages and PR descriptions in plain, first-person prose, the way the maintainer does (see `git log --author=Fitzgerald` and PRs #123, #124, #125): + +- Subject: short, lowercase, imperative ("allow fakes to be generated into the interface's own package"). No conventional-commit prefixes. +- Body only when the diff does not explain itself: what was wrong, what changes for the user. A few sentences, not headings or bullet inventories of files and functions. +- `Fixes #n` on its own line at the end when it closes an issue. +- PR body: describe the problem and the outcome for users, not the commit list. No commit hashes, no narrating the process (do not mention TDD, red/green, or how carefully it was tested; just say what is covered). Mention issues that are fixed or related at the end. +- Squash follow-up tweaks into the commit they belong to before pushing. +- No AI attribution or co-author lines. + +## Contribution constraints (from README) + +Keep `main.go` simple, avoid adding CLI options, avoid adding internal complexity, and keep unit coverage high. diff --git a/vendor/github.com/maxbrunsfeld/counterfeiter/v6/README.md b/vendor/github.com/maxbrunsfeld/counterfeiter/v6/README.md index dd36898987..0b6546f80f 100644 --- a/vendor/github.com/maxbrunsfeld/counterfeiter/v6/README.md +++ b/vendor/github.com/maxbrunsfeld/counterfeiter/v6/README.md @@ -1,67 +1,54 @@ -# `counterfeiter` [![GitHub Actions](https://github.com/maxbrunsfeld/counterfeiter/actions/workflows/go.yml/badge.svg)](https://github.com/maxbrunsfeld/counterfeiter/actions/workflows/go.yml) [![Go Report Card](https://goreportcard.com/badge/github.com/maxbrunsfeld/counterfeiter/v6)](https://goreportcard.com/report/github.com/maxbrunsfeld/counterfeiter/v6) [![GoDoc](https://godoc.org/github.com/maxbrunsfeld/counterfeiter/v6?status.svg)](https://godoc.org/github.com/maxbrunsfeld/counterfeiter/v6) +# `counterfeiter` [![GitHub Actions](https://github.com/maxbrunsfeld/counterfeiter/actions/workflows/go.yml/badge.svg)](https://github.com/maxbrunsfeld/counterfeiter/actions/workflows/go.yml) [![Go Reference](https://pkg.go.dev/badge/github.com/maxbrunsfeld/counterfeiter/v6.svg)](https://pkg.go.dev/github.com/maxbrunsfeld/counterfeiter/v6) -When writing unit-tests for an object, it is often useful to have fake implementations -of the object's collaborators. In go, such fake implementations cannot be generated -automatically at runtime, and writing them by hand can be quite arduous. +Go code declares what it needs from its dependencies as interfaces, usually small ones +defined by the package that uses them. 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. -`counterfeiter` allows you to simply generate test doubles for a given interface. +`counterfeiter` generates test doubles for a given interface. Given this: -### Supported Versions Of `go` - -`counterfeiter` follows the [support policy of `go` itself](https://golang.org/doc/devel/release.html#policy): - -> Each major Go release is supported until there are two newer major releases. For example, Go 1.5 was supported until the Go 1.7 release, and Go 1.6 was supported until the Go 1.8 release. We fix critical problems, including [critical security problems](https://golang.org/security), in supported releases as needed by issuing minor revisions (for example, Go 1.6.1, Go 1.6.2, and so on). - -If you are having problems with `counterfeiter` and are not using a supported version of go, please update to use a supported version of go before opening an issue. - -### Using `counterfeiter` +```go +package foo -⚠️ Please use [`go modules`](https://blog.golang.org/using-go-modules) when working with counterfeiter. +//go:generate go tool counterfeiter -generate -Typically, `counterfeiter` is used in `go generate` directives. It can be frustrating when you change your interface declaration and suddenly all of your generated code is suddenly out-of-date. The best practice here is to use the [`go generate` command](https://blog.golang.org/generate) to make it easier to keep your test doubles up to date. +//counterfeiter:generate . MySpecialInterface +type MySpecialInterface interface { + DoThings(string, uint64) (int, error) +} +``` -⚠️ If you are working with go 1.23 or earlier, please refer to an [older version of this README](https://github.com/maxbrunsfeld/counterfeiter/blob/e39cbe6aaa94a0b6718cf3d413cd5319c3a1f6fa/README.md#using-counterfeiter), as the instructions below assume go 1.24 (which added `go tool` support) and later. +`go generate` writes `foofakes/fake_my_special_interface.go`, and your tests can do this: -#### Step 1 - Add `counterfeiter` as a tool dependency +```go +fake := &foofakes.FakeMySpecialInterface{} +fake.DoThingsReturns(3, nil) -Establish a tool dependency on counterfeiter by running the following command: +num, err := fake.DoThings("stuff", 5) -```shell -go get -tool github.com/maxbrunsfeld/counterfeiter/v6 +Expect(num).To(Equal(3)) +Expect(fake.DoThingsCallCount()).To(Equal(1)) +str, _ := fake.DoThingsArgsForCall(0) +Expect(str).To(Equal("stuff")) ``` -#### Step 2a - Add `go:generate` Directives - -You can add directives right next to your interface definitions (or not), in any `.go` file in your module. +## Getting Started -```shell -$ cat myinterface.go -``` +`counterfeiter` is run by [`go generate`](https://go.dev/blog/generate), so fakes are regenerated alongside the code they fake. The steps below assume go 1.24 or later, which added `go tool`; for older versions of go, refer to an [older version of this README](https://github.com/maxbrunsfeld/counterfeiter/blob/e39cbe6aaa94a0b6718cf3d413cd5319c3a1f6fa/README.md#using-counterfeiter). -```go -package foo - -//go:generate go tool counterfeiter . MySpecialInterface +### Step 1 - Add `counterfeiter` as a tool dependency -type MySpecialInterface interface { - DoThings(string, uint64) (int, error) -} -``` +Establish a tool dependency on counterfeiter by running the following command: ```shell -$ go generate ./... -Writing `FakeMySpecialInterface` to `foofakes/fake_my_special_interface.go`... Done +go get -tool github.com/maxbrunsfeld/counterfeiter/v6 ``` -#### Step 2b - Add `counterfeiter:generate` Directives +### Step 2 - Add directives -If you plan to have many directives in a single package, consider using this -option, as it will speed things up considerably. You can add directives right -next to your interface definitions (or not), in any `.go` file in your module. +Add one `go:generate` directive per package that runs `counterfeiter -generate`, and one `counterfeiter:generate` directive per interface you want a fake for. You can add them right next to your interface definitions (or not), in any `.go` file in the package. -```shell -$ cat myinterface.go -``` +In `myinterface.go`: ```go package foo @@ -82,71 +69,174 @@ type MyOtherInterface interface { } ``` +A `counterfeiter:generate` directive takes the same arguments as the command line: the directory of the package that declares the interface (`.` for the package the directive is in) and the name of the interface, plus any of the flags described below. + +If a package only has an interface or two, you can skip `-generate` and the `counterfeiter:generate` directives, and run `counterfeiter` directly from one `go:generate` line per interface instead: + +```go +//go:generate go tool counterfeiter . MySpecialInterface +``` + +`-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. + +### Step 3 - Run `go generate` + +You can run `go generate` in the directory with your directive, or in the root of your module (to ensure you generate for all packages in your module): + ```shell $ go generate ./... Writing `FakeMySpecialInterface` to `foofakes/fake_my_special_interface.go`... Done Writing `FakeMyOtherInterface` to `foofakes/fake_my_other_interface.go`... Done ``` -#### Step 3 - Run `go generate` +## Defaults -You can run `go generate` in the directory with your directive, or in the root of your module (to ensure you generate for all packages in your module): +`MySpecialInterface` above is declared in package `foo`, so `//counterfeiter:generate . MySpecialInterface` produces: + +| | default | change it with | +|---|---|---| +| fake type | `FakeMySpecialInterface` (`Fake` + the interface name) | `-fake-name`, `-fake-name-template` | +| file | `fake_my_special_interface.go` (the fake's name in snake case) | `-o .go` | +| directory | `foofakes/` (`fakes`, beside the package that declares the interface) | `-o ` | +| package | `foofakes` (`fakes`), or whatever package already lives in the output directory | `-o`, `-test`, or a file declaring the package | + +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. + +## Common Setups + +The examples below are `counterfeiter:generate` directives, and assume the package has the `//go:generate go tool counterfeiter -generate` line from Step 2. A package needs only one of those, no matter how many directives it has. + +### Shared settings for every directive in a package + +Flags given alongside `-generate` on the `//go:generate` line are the defaults for every `//counterfeiter:generate` directive in the package: `-o`, `-header`, `-q`, `-test` and `-fake-name-template`. A directive's own flags take precedence. So if you would rather keep all of a package's fakes in a `fake` package, named after their interfaces, you can write that once: + +```go +//go:generate go tool counterfeiter -generate -o fake -fake-name-template '{{.TargetName}}' + +//counterfeiter:generate . MyRepository +//counterfeiter:generate . MyPresenter +``` ```shell $ go generate ./... +Writing `MyRepository` to `fake/my_repository.go`... Done +Writing `MyPresenter` to `fake/my_presenter.go`... Done ``` -#### Invoking `counterfeiter` from the shell +### Interfaces from other packages, the standard library or third-party modules -You can use the following command to invoke `counterfeiter` from within a go module: +You can fake any interface your module can import. A third-party module has to be a dependency first (`go get` it, so it appears in your `go.mod`), since `counterfeiter` loads the interface from the module cache the same way the compiler would. Name the interface as `.`, or give the directory of the package and the interface name: -```shell -$ go tool counterfeiter +```go +//counterfeiter:generate io.WriteCloser +//counterfeiter:generate github.com/redis/go-redis/v9.Pipeliner +//counterfeiter:generate ../otherpackage OtherInterface +``` -USAGE - counterfeiter - [-generate>] [-o ] [-p] [--fake-name ] - [-header ] - [] [-] +The two forms differ in where the fake goes. With `.`, it goes into the fakes package of the directory the directive is in, so in package `foo` the first line writes `foofakes/fake_write_closer.go`. With a directory and an interface name, it goes into the fakes package beside that directory, so the third line writes `../otherpackage/otherpackagefakes/fake_other_interface.go`. Use `-o` to put it somewhere else. + +### Fakes for tests inside the interface's own package + +By default the fake lives in a sibling `fakes` package. Tests in `` itself cannot import it, because `fakes` imports `` and that would be an import cycle. To use a fake in those tests, point `-o` at the interface's own directory: + +```go +//counterfeiter:generate -o . . MySpecialInterface ``` -#### Installing `counterfeiter` to `$GOPATH/bin` +When the output directory is the directory of the package that declares the interface, `counterfeiter` generates the fake as a member of that package: it does not import the package, refers to its types unqualified, and can fake unexported interfaces too, in which case the fake is unexported as well (`gadget` gets `fakeGadget`) unless `-fake-name` names it. `-o` may also name a file in that directory, for example `-o fake_my_special_interface_test.go` to keep the fake out of the non-test build. -This is unnecessary if you're using the approach described above, but does allow you to invoke `counterfeiter` in your shell _outside_ of a module: +### Fakes for tests in `_test` -```shell -$ go install github.com/maxbrunsfeld/counterfeiter/v6 -$ ~/go/bin/counterfeiter +Go lets a directory hold a second package for tests, `_test`, which imports `` and sees only its exported API. If your tests are in it, `-test` generates the fake into that external test package instead, as a `_test.go` file in the current directory, next to the tests that use it: -USAGE - counterfeiter - [-generate>] [-o ] [-p] [--fake-name ] - [-header ] - [] [-] +```go +//counterfeiter:generate -test . MySpecialInterface +//counterfeiter:generate -test ../otherpackage OtherInterface +//counterfeiter:generate -test io.WriteCloser ``` -### Generating Test Doubles +In package `foo`, all three of these write a `_test.go` file into the current directory, in package `foo_test`: `fake_my_special_interface_test.go`, `fake_other_interface_test.go` and `fake_write_closer_test.go`. With `-test` the fake goes into the package the directive is in, not the package that declares the interface, so faking `OtherInterface` writes nothing into `../otherpackage`. The fakes are only compiled for tests, and the tests use them unqualified (`&FakeMySpecialInterface{}`). The interface's package is imported as usual, so the interface must be exported. With `-o ` the fake goes into the external test package of that directory instead. -Given a path to a package and an interface name, you can generate a test double. +### A fakes package whose name is not its directory name + +If the output directory already contains Go files, the fake joins that package. If it is empty, the fake's package is named after the directory. So to name the package differently from its directory, say `impl_fakes` in `fakes/`, add a file declaring that package first: + +```go +// fakes/doc.go +package impl_fakes +``` + +```go +//counterfeiter:generate -o fakes . MyInterface +``` + +### Naming fakes + +`-fake-name` names one fake. `-fake-name-template` is a Go `text/template` in which `{{.TargetName}}` is the name of the interface being faked (first letter upper-cased); it applies wherever `-fake-name` is not given, and can be set once for the package on the `-generate` line. The file name follows the fake's name. + +```go +//counterfeiter:generate -fake-name Repo . MyRepository +//counterfeiter:generate -fake-name-template '{{.TargetName}}Double' . MyPresenter +``` ```shell -$ cat path/to/foo/file.go +$ go generate ./... +Writing `Repo` to `foofakes/repo.go`... Done +Writing `MyPresenterDouble` to `foofakes/my_presenter_double.go`... Done ``` +### A header on every fake + +`-header` prepends the contents of a file to every generated fake, for a licence header for example. Set it once on the `-generate` line, or per directive. + ```go -package foo +//go:generate go tool counterfeiter -generate -header ../LICENSE.header +``` -type MySpecialInterface interface { - DoThings(string, uint64) (int, error) -} +### Faking a function type + +Function types can be faked as well. The fake is a struct whose `Spy` method has the function's signature, so `fake.Spy` goes wherever the function is expected. `Returns`, `CallCount`, `ArgsForCall` and the rest work as for a method, without a method name in front. + +```go +//counterfeiter:generate . RequestHandler +type RequestHandler func(*http.Request) error +``` + +```go +fake := &foofakes.FakeRequestHandler{} +fake.Returns(nil) + +server := NewServer(fake.Spy) +``` + +### Faking a whole package + +Package mode, `-p`, is for code that calls package-level functions directly, `os.Hostname()` say, and has no interface to fake. It writes a file with an interface whose methods are the package's exported functions, and a shim struct that forwards each method to the package. The file carries a `counterfeiter:generate` directive of its own, so the next `go generate` produces a fake of that interface. + +```go +//counterfeiter:generate -p os ``` ```shell -$ go tool counterfeiter path/to/foo MySpecialInterface -Wrote `FakeMySpecialInterface` to `path/to/foo/foofakes/fake_my_special_interface.go` +$ go generate ./... +Writing `Os` to `osshim/os.go`... Done +$ go generate ./... +Writing `FakeOs` to `osshimfakes/fake_os.go`... Done +``` + +Your code takes the `osshim.Os` interface, production code passes `&osshim.OsShim{}`, tests pass the fake. + +### Printing to stdout, quieter output + +A trailing `-` prints the fake to standard output instead of writing a file: + +```shell +$ go tool counterfeiter . MySpecialInterface - ``` -### Using Test Doubles In Your Tests +`-q` drops the `Writing ...` lines, which is useful in a `-generate` line for a package with many fakes. + +## Using Test Doubles In Your Tests Instantiate fakes: @@ -156,6 +246,44 @@ import "my-repo/path/to/foo/foofakes" var fake = &foofakes.FakeMySpecialInterface{} ``` +For each method `` of the interface, the fake has: + +| | | +|---|---| +| `Returns`, `ReturnsOnCall` | stub what it returns | +| `Calls`, `Stub` | replace it with a function | +| `CallCount`, `ArgsForCall` | inspect the calls it received | + +You can stub return values: + +```go +fake.DoThingsReturns(3, errors.New("the-error")) + +num, err := fake.DoThings("stuff", 5) +Expect(num).To(Equal(3)) +Expect(err).To(Equal(errors.New("the-error"))) +``` + +or stub them for one call at a time, counting from zero; calls without an entry fall back to `Returns`: + +```go +fake.DoThingsReturnsOnCall(0, 1, nil) +fake.DoThingsReturnsOnCall(1, 0, errors.New("the-error")) +``` + +When the result depends on the arguments, give the fake a function instead. It takes precedence over `Returns` and `ReturnsOnCall`, and calling either of those clears it again: + +```go +fake.DoThingsCalls(func(s string, n uint64) (int, error) { + if s == "stuff" { + return 3, nil + } + return 0, errors.New("unexpected") +}) +``` + +`DoThingsCalls` stores the function in the exported `DoThingsStub` field while holding the fake's lock, so it is safe even if other goroutines are already calling the fake. Setting the field directly is only useful in a struct literal, `&foofakes.FakeMySpecialInterface{DoThingsStub: ...}`. Without a stub or stubbed return values, the fake returns zero values. + Fakes record the arguments they were called with: ```go @@ -168,40 +296,196 @@ Expect(str).To(Equal("stuff")) Expect(num).To(Equal(uint64(5))) ``` -You can stub their return values: +Slice and array arguments are recorded as copies, so a caller that reuses its buffer does not change what the fake recorded; a stub function still receives the original. `fake.Invocations()` returns every recorded call of every method, keyed by method name. Fakes are safe to use from several goroutines at once. -```go -fake.DoThingsReturns(3, errors.New("the-error")) +For more examples of using the `counterfeiter` API, look at [some of the provided examples](generated_fakes_test.go). -num, err := fake.DoThings("stuff", 5) -Expect(num).To(Equal(3)) -Expect(err).To(Equal(errors.New("the-error"))) +## Command reference + +`go tool counterfeiter -help` prints the following. Outside a module, `go install github.com/maxbrunsfeld/counterfeiter/v6@latest` puts a `counterfeiter` binary in `$GOPATH/bin` that takes the same arguments. + +```text +USAGE + counterfeiter + [-generate] [-o ] [-p] [-fake-name ] + [-fake-name-template