Use whenever the task is to write, add, fix or review Go tests, even a quick table-driven test for one function: t.Run subtests, t.Parallel, helpers, fuzz tests, benchmarks, race detection and coverage with go test, plus Ginkgo and gomock or testify mocks.
94%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Companion reference for go-unit-tests. A test double (per
ISTQB Glossary) replaces a real dependency so the subject
under test runs in isolation. Use when a unit test reaches a database,
HTTP client, file system, or any interface boundary; for tests that do not
cross an interface boundary, prefer real objects or simple hand-written
stubs without a mocking library.
| Tool | Approach |
|---|---|
go.uber.org/mock (gomock + mockgen) | Codegen from interface |
github.com/stretchr/testify/mock | Hand-written stub struct |
go get go.uber.org/mock/gomock
go install go.uber.org/mock/mockgen@latest
# Source mode: generates from a .go file
mockgen -source=internal/store/store.go \
-destination=internal/store/mock_store.go \
-package=store
# Package mode: package + interface names
mockgen github.com/myorg/myapp/internal/store Store,Querier \
> internal/store/mock_store.goAdd a //go:generate mockgen ... directive so go generate ./... keeps
mocks in sync (generated mocks go stale when the interface changes - run
it in CI to catch drift). The -typed flag emits type-safe
Return/Do/DoAndReturn helpers.
Per pkg.go.dev/go.uber.org/mock/gomock:
func TestOrderService_Submit(t *testing.T) {
ctrl := gomock.NewController(t)
// ctrl.Finish() runs automatically via t.Cleanup when *testing.T is passed.
mockStore := store.NewMockStore(ctrl)
mockStore.EXPECT().
SaveOrder(gomock.Any()).
Return(nil).
Times(1)
svc := NewOrderService(mockStore)
if err := svc.Submit(Order{ID: "abc"}); err != nil {
t.Fatalf("unexpected error: %v", err)
}
}| Matcher | Behaviour |
|---|---|
gomock.Any() | Any argument value |
gomock.Eq(v) | Deep equality |
gomock.Nil() / gomock.Not(m) | Nil / negation |
gomock.AssignableToTypeOf(v) | Type-assignability |
gomock.InAnyOrder(s) | Slice elements in any order |
gomock.Regex(re) | String matches regexp |
mockStore.EXPECT().FindByID(gomock.Any()).Return(nil, ErrNotFound).Times(2)
mockStore.EXPECT().Ping().MinTimes(1).MaxTimes(3)
mockStore.EXPECT().Metrics().AnyTimes()
gomock.InOrder(
mockStore.EXPECT().Begin(),
mockStore.EXPECT().SaveOrder(gomock.Any()).Return(nil),
mockStore.EXPECT().Commit(),
)Per github.com/stretchr/testify and
pkg.go.dev/github.com/stretchr/testify/mock - embed
mock.Mock and implement the interface by hand:
type MockNotifier struct {
mock.Mock
}
func (m *MockNotifier) Send(to, body string) error {
args := m.Called(to, body)
return args.Error(0)
}
func TestAlertService_Notify(t *testing.T) {
n := new(MockNotifier)
n.On("Send", "ops@example.com", mock.Anything).Return(nil)
svc := NewAlertService(n)
if err := svc.Notify("ops@example.com", "disk full"); err != nil {
t.Fatalf("unexpected error: %v", err)
}
n.AssertExpectations(t) // every On(...) expectation was exercised
}mock.Anything ≈ gomock.Any(). Also:
n.AssertCalled(t, "Send", ...) / n.AssertNotCalled(t, "Send").
| Concern | gomock | testify/mock |
|---|---|---|
| Mock generation | mockgen codegen | Hand-written |
| Argument matching | Rich matcher library | mock.Anything + basic |
| Ordering | InOrder/After | Not built-in |
| Dependency | Two packages | One package |
gomock when strict call-order or exhaustive matching matters;
testify/mock when the team already uses testify/assert and wants one
dependency (hand-written stubs must be updated manually on interface
change).
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Mock every dependency | Tests verify mock wiring, not behavior | Mock only true isolation boundaries |
Forget AssertExpectations (testify) | Uncalled On(...) passes silently | Always call it at the end |
Manual ctrl.Finish() (gomock) | Redundant with NewController(t) | Remove |
AnyTimes() everywhere | Hides missing invocations | Default to Times(1) / MinTimes(1) |
rust-unit-tests
references/rust-mocking.md