# minimock: Go mocks generated from interface declarations, and what they cost you

> minimock reads Go interface declarations and emits statically typed mocks that plug into the standard testing package, with helpers for expectations, invocation counters and a go:generate line that keeps generated files in step with the source interface.

**gojuno/minimock** — Powerful mock generation tool for Go programming language

- Repository: https://github.com/gojuno/minimock
- Stars: 752 · Forks: 49
- Language: Go
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/gojuno-minimock

## minimock turns interface declarations into typed test doubles

minimock generates mocks out of Go interface declarations, and the whole design follows from that one sentence. The mocks it writes are statically typed, so a test does not need type assertions to reach a mocked method, and they are integrated with the standard Go testing package through a controller built from the testing.T value. The README lists the supporting features: readiness for Go modules, support for generics, the ability to generate several mocks in one run, and generation from interface aliases. It also states that the generated code passes a default set of golangci-lint checks, which matters more than it sounds, because a generated file that trips the linter is a file every consumer of the mock has to silence. For engineers who write Go tests against code that depends on interfaces, the problem being solved is specific: writing a fake that fails the test when the wrong call arrives, and noticing when an expected call never happened.

## Installing the v3 binary, and the module path that pins it

The installation section splits by build system, and the split is the first thing to understand. If you use Go modules, the README points to the latest binary on the releases page, or to a source install:

```bash
go install github.com/gojuno/minimock/v3/cmd/minimock@latest
```

If you do not use Go modules, the README directs you to the latest v2.x binary or to the v2 branch instead. That major version difference is not cosmetic, because the module path carries the version in its last segment, and the generated mocks are built against the controller of the same major version. The module file records go 1.23.0 as the language requirement and names toolchain go1.24.6, so a machine with an older Go release will not build it. The dependencies are also visible in the module file: golang.org/x/tools, google.golang.org/protobuf, github.com/stretchr/testify, github.com/hexdigest/gowrap and github.com/pkg/errors among them.

## Pointing the generator at one interface with -i and -o

The command line is small and the flags are worth reading in full before you run it. The -i flag takes comma-separated names of the interfaces to mock, for example fmt.Stringer and io.Reader, and it defaults to every interface in the package. The io.* notation generates mocks for all interfaces in that package. The -o flag takes comma-separated destination file names or packages, and by default the generated mock is placed in the source package directory. The README walks through three equivalent ways to reach one interface, all from the same directory:

```bash
$ cd ~/go/src/github.com/gojuno/minimock/tests
$ minimock 
```

Running the command with no flags generates mocks for all interfaces defined in the tests package. Passing -i Formatter, using the relative form -i ./tests.Formatter, or using the full import path with -i github.com/gojuno/minimock/tests.Formatter -o ./tests/ each produce the same result, which the README states is the file ./tests/formatter_mock_test.go.

## Chaining expectations with the builder pattern

The generated mock exposes one field per interface method, and each field carries the helpers for that method. The basic shape takes a controller and returns the expectation object:

```go
mc := minimock.NewController(t)
formatterMock := NewFormatterMock(mc).FormatMock.Expect("hello %s!", "world").Return("hello world!")
```

The README recommends the builder pattern when more than one method has to be set up, and the payoff is a single expression that configures an entire interface. For an interface with a Read and a Close method, the setup becomes one line:

```go
mc := minimock.NewController(t)
readCloserMock := NewReadCloserMock(mc).ReadMock.Expect([]byte(1,2,3)).Return(3, nil).CloseMock.Return(nil)
```

Because the helpers are chained rather than configured through separate calls, the compiler sees the argument types directly, which is the point of the statically typed approach. The controller is what makes a missing call fail the test instead of passing silently.

## Inspect, ExpectParams, When/Then and Set cover four different intents

Expect and Return is only one of the shapes the generated code supports, and the alternatives exist for cases where a strict full-argument match is the wrong tool. The Inspect helper runs a callback over the received arguments, so a test can assert on one element of a slice:

```go
mc := minimock.NewController(t)
readCloserMock := NewReadCloserMock(mc).ReadMock.Inspect(func(p []byte){
  assert.Equal(mc, 2, p[1])
}).Return(3, nil).CloseMock.Return(nil)
```

For methods with many arguments, ExpectParams helpers are generated per argument, such as ExpectIntArgParam1 and ExpectFloatArgParam3, so only the arguments you care about are checked. When/Then pairs let one mock answer several calls:

```go
mc := minimock.NewController(t)
formatterMock := NewFormatterMock(mc)
formatterMock.FormatMock.When("Hello %s!", "world").Then("Hello world!")
formatterMock.FormatMock.When("Hi %s!", "there").Then("Hi there!")
```

And Set installs a plain function, which is the way to compute a return value from the received arguments. The README also documents invocation counters, with a BeforeFormatCounter accessor on the mock, so a stub can vary its answer by call number, and a Times helper for expecting an exact number of calls, although the example for Times is cut off in the README.

## The go:generate line that keeps mocks in step with the interface

The regeneration story is where minimock differs from a one-off code generator. By default the tool puts a go:generate instruction into the generated code, so all you need to do when the source interface is updated is to run go generate ./... from within the project directory. Two flags tune that behaviour. The -g flag says do not put the go:generate instruction into the generated code, for projects that prefer to invoke the generator from a script. The -gr flag changes the go:generate line from the minimock args form to a go run github.com/gojuno/minimock/v3/cmd/minimock form, which the README describes as useful while controlling the minimock version with go mod. That is the version pinning story: the generator version travels with the module rather than with whichever binary happens to be on the developer's PATH. The repository's own Makefile demonstrates the other style, invoking go run ./cmd/minimock/minimock.go once per interface with -i and -o for each target file.

## Where a hand-written fake is the better answer

The honest comparison here is not with another mock library but with the fake you could write yourself. A hand-written stub is a struct with methods that return zero values, and it costs nothing to generate and nothing to maintain in a small interface, but it gives you no argument checking, no call counters, and no failure when a method is never called. minimock trades that simplicity for a generated file, a controller in every test, and an expectation that all mocked methods have been called during the test. The README also notes that minimock can be used with the GoUnit tool, which generates table-driven tests that make use of minimock, so the generated doubles fit the table driven style rather than fighting it. The point where minimock becomes the wrong tool is a project where the dependency you want to fake is a concrete struct rather than an interface, because the generator only reads interface declarations, and a project that needs the fake in a separate package, because the default suffix _mock_test.go puts the file in the test binary of the package that owns it.

## MIT licensed, and versioned in the module path

minimock is MIT licensed, which makes it straightforward to take into a codebase without licence negotiation, and the licence covers the generator rather than any code you generate with it. The generated files are yours, in the sense that they are ordinary Go source in your repository, and the -pr and -s flags let you control the mock file prefix and suffix. On maintenance, the last push to the repository was on 2026-10-21, while the most recent tagged release is v3.4.7 from 2025-09-04, preceded by v3.4.6 and v3.4.5. That gap between the last commit and the last release means the default branch can move ahead of the tagged binary, which is one reason the -gr flag and the go run form of the go:generate line exist. Upgrade cost is low for Go module users, who can move the module requirement, and it is higher for the v2 path, which is documented separately.

## Conclusion

minimock is worth adopting in a Go service that keeps its dependencies behind interfaces and wants every mocked call to be asserted, since the generated controller checks that all mocked methods were called and the counters let you script behaviour by call number. It is the wrong tool for code that passes concrete structs around, for projects that have not moved to Go modules, and for teams that need a shared fake package, because the default _mock_test.go suffix places the generated file inside the test binary of the package that owns it. Verify first that your Go toolchain satisfies the go 1.23.0 requirement with toolchain go1.24.6 recorded in go.mod, then decide whether to run the generator as a module dependency using the -gr flag or as a separate binary, because those two choices decide which minimock version writes your mocks.

## FAQ

### How do I install minimock for a Go modules project?

The README gives two options: download the latest binary from the releases page, or install from source with go install github.com/gojuno/minimock/v3/cmd/minimock@latest. Projects that do not use Go modules are pointed to the latest v2.x binary or the v2 branch.

### How do I generate a mock for a single interface?

Pass the interface name to the -i flag, for example minimock -i Formatter, and use -o to choose the destination. The relative form -i ./tests.Formatter and the full import path form -i github.com/gojuno/minimock/tests.Formatter -o ./tests/ produce the same generated file, ./tests/formatter_mock_test.go.

### What happens if a mocked method is never called in my test?

minimock makes sure that all mocked methods have been called during the test, so an unused expectation is reported rather than silently ignored. There is also a Times helper for setting the expected number of calls.

### How do I regenerate mocks after changing an interface?

The generated code carries a go:generate instruction, so running go generate ./... from the project directory refreshes the mocks. The -g flag omits that instruction, and the -gr flag rewrites it to run the generator through go run github.com/gojuno/minimock/v3/cmd/minimock so the version is controlled by go mod.

## Sources

- [gojuno/minimock on GitHub](https://github.com/gojuno/minimock)
- [Issues](https://github.com/gojuno/minimock/issues)
- [License: MIT](https://github.com/gojuno/minimock/blob/master/LICENSE)
- [README](https://github.com/gojuno/minimock/blob/master/README.md)
- [Releases](https://github.com/gojuno/minimock/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/gojuno-minimock
