Define interfaces for dependencies and implement them with simple structs in tests that return preset values or record calls. This is idiomatic Go and requires no reflection.
Hand-written mocks: preferred for small interfaces (1-3 methods) — simple and readable
testify/mock: adds call expectations and assertion helpers for complex interaction testing
gomock (mockgen): generates mocks from interfaces — useful for large interfaces with many methods
Avoid over-mocking: if your test mocks everything, it tests nothing — prefer integration tests for complex scenarios
Functional options on mocks: allow test-specific behavior without modifying the mock struct