Mockery is a popular mock generator for Go. I appreciate the effort behind it - the maintainers do great work. But starting with v3, the project seems to have taken a wrong turn.
Issue 1: A mock generator trying to be something bigger
Mockery v3 now requires a dedicated YAML configuration file:
with-expecter: true
packages:
github.com/your/project/internal/service:
interfaces:
UserRepository:
OrderService:
config:
with-expecter: false
//go:generate is no longer supported - you must use the config file. A mock generator shouldn’t need this. It should read interfaces and produce mocks. That’s it.
Issue 2: Hidden race conditions
Mockery uses testify/mock under the hood, which stores references to call arguments. Here’s what happens:
- When a mock is called, testify saves argument references in its internal
Callsslice - Arguments are stored until the test finishes
- At test cleanup,
AssertExpectationsaccesses stored arguments again
With sync.Pool and parallel tests, this causes race conditions.
Service method that uses a pool:
var bufPool = sync.Pool{
New: func() any { return new(bytes.Buffer) },
}
func (s *Service) Send(msg Message) error {
buf := bufPool.Get().(*bytes.Buffer)
defer bufPool.Put(buf)
buf.Reset()
buf.WriteString(msg.Body)
return s.sender.Send(buf) // passing pooled buffer to dependency
}
Test with Mockery:
func TestSend(t *testing.T) {
mockSender := NewMockSender(t)
mockSender.EXPECT().
Send(mock.Anything). // Mockery holds reference to buf
Return(nil)
svc := &Service{sender: mockSender}
_ = svc.Send(Message{Body: "hello"})
// buf returned to pool, but Mockery still references it
// next test or goroutine reuses buf → race condition
}
Flaky tests that are hard to diagnose. The -race flag will catch it, but only if your test coverage triggers the race.
Alternative
Consider mockgen - simple, no configuration files, more idiomatic Go. It does one thing and does it well. And it doesn’t use testify, so it doesn’t have this specific issue.