| name | generating-mocks |
| description | Generate mock implementations using mockery for testing. Use when creating mocks, updating mock interfaces, or setting up test doubles. |
| allowed-tools | Read, Write, Edit, Bash |
Generating Mocks
This skill guides you through generating and managing mock implementations using mockery.
Quick Commands
make mock-gen # Generate all mocks
make mock-clean # Remove all generated mocks
Mockery Configuration
Configuration is in .mockery.yaml:
with-expecter: true # Enable EXPECT() style assertions
testonly: false # Don't add //go:build test constraint
all: false # Don't auto-generate for all interfaces
packages:
app/internal/shared/domain/repository:
interfaces:
UserRepository:
config:
dir: internal/mocks/repository
outpkg: mocks
app/internal/features/auth/usecase:
interfaces:
AuthUsecase:
config:
dir: internal/mocks/usecase
outpkg: mocks
Adding a New Interface to Mock
Step 1: Define the Interface
Create your interface in the appropriate package:
// internal/features/product/usecase/product_usecase.go
package usecase
import "app/internal/features/product/delivery/http/dto"
type ProductUsecase interface {
CreateProduct(req *dto.CreateProductRequest) (*dto.ProductResponse, error)
GetProductByID(id string) (*dto.ProductResponse, error)
ListProducts(page, perPage int) ([]dto.ProductResponse, int64, error)
UpdateProduct(id string, req *dto.UpdateProductRequest) (*dto.ProductResponse, error)
DeleteProduct(id string) error
}
Step 2: Add to .mockery.yaml
Add the interface to .mockery.yaml:
packages:
# ... existing packages ...
app/internal/features/product/usecase:
interfaces:
ProductUsecase:
config:
dir: internal/mocks/usecase
outpkg: mocks
Step 3: Generate Mocks
make mock-gen
This creates: internal/mocks/usecase/mock_ProductUsecase.go
Step 4: Use the Mock in Tests
package handler
import (
mocks "app/internal/mocks/usecase"
"testing"
"github.com/stretchr/testify/assert"
)
func TestProductHandler_CreateProduct(t *testing.T) {
// Create mock
mockUsecase := mocks.NewMockProductUsecase(t)
// Set expectations
mockUsecase.EXPECT().
CreateProduct(mock.AnythingOfType("*dto.CreateProductRequest")).
Return(&dto.ProductResponse{ID: "123"}, nil).
Once()
// Use mock in tests...
handler := NewProductHandler(mockUsecase)
// ... test code ...
// Verify expectations
mockUsecase.AssertExpectations(t)
}
Mock Patterns
Repository Mocks
Repository interfaces are typically in internal/shared/domain/repository/:
packages:
app/internal/shared/domain/repository:
interfaces:
ProductRepository:
config:
dir: internal/mocks/repository
outpkg: mocks
Generated mock: internal/mocks/repository/mock_ProductRepository.go
Use Case Mocks
Use case interfaces are in feature-specific packages:
packages:
app/internal/features/product/usecase:
interfaces:
ProductUsecase:
config:
dir: internal/mocks/usecase
outpkg: mocks
Generated mock: internal/mocks/usecase/mock_ProductUsecase.go
Using Generated Mocks
Basic Expectations
// Exact arguments
mockRepo.EXPECT().
FindByID("123").
Return(user, nil).
Once()
// Any arguments
mockRepo.EXPECT().
Create(mock.AnythingOfType("*entity.User")).
Return(nil).
Once()
// Multiple calls
mockRepo.EXPECT().
List().
Return([]entity.User{}, nil).
Times(3)
Argument Matchers
import "github.com/stretchr/testify/mock"
// Match any value of type
mock.AnythingOfType("string")
mock.AnythingOfType("*entity.User")
// Custom matcher
mockRepo.EXPECT().
Create(mock.MatchedBy(func(u *entity.User) bool {
return u.Email == "test@example.com"
})).
Return(nil).
Once()
// Anything (any type)
mockRepo.EXPECT().
Update(mock.Anything).
Return(nil).
Once()
Return Values
// Return single value
mockRepo.EXPECT().
Delete(userID).
Return(nil).
Once()
// Return multiple values
mockRepo.EXPECT().
FindByEmail("test@example.com").
Return(user, nil).
Once()
// Return error
mockRepo.EXPECT().
FindByID("invalid").
Return(nil, gorm.ErrRecordNotFound).
Once()
// Different returns for sequential calls
mockRepo.EXPECT().FindAll().Return([]User{user1}, nil).Once()
mockRepo.EXPECT().FindAll().Return([]User{user1, user2}, nil).Once()
Call Counts
// Called exactly once (default)
mockRepo.EXPECT().Create(user).Return(nil).Once()
// Called exactly N times
mockRepo.EXPECT().Update(user).Return(nil).Times(3)
// Called any number of times (including 0)
mockRepo.EXPECT().GetConfig().Return("config").Maybe()
Complete Example
1. Define Interface
// internal/features/notification/usecase/notification_usecase.go
package usecase
type NotificationUsecase interface {
SendEmail(to, subject, body string) error
SendSMS(to, message string) error
}
2. Add to .mockery.yaml
packages:
app/internal/features/notification/usecase:
interfaces:
NotificationUsecase:
config:
dir: internal/mocks/usecase
outpkg: mocks
3. Generate Mock
make mock-gen
4. Use in Test
package usecase
import (
"app/internal/features/notification/usecase"
mocks "app/internal/mocks/usecase"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
func TestUserUsecase_RegisterWithNotification(t *testing.T) {
// Setup mocks
mockUserRepo := mocks.NewMockUserRepository(t)
mockNotification := mocks.NewMockNotificationUsecase(t)
userUsecase := NewUserUsecase(mockUserRepo, mockNotification)
// Set expectations
mockUserRepo.EXPECT().
Create(mock.AnythingOfType("*entity.User")).
Return(nil).
Once()
mockNotification.EXPECT().
SendEmail(
"newuser@example.com",
"Welcome!",
mock.AnythingOfType("string"),
).
Return(nil).
Once()
// Execute test
err := userUsecase.Register(&RegisterRequest{
Email: "newuser@example.com",
})
// Assert
assert.NoError(t, err)
mockUserRepo.AssertExpectations(t)
mockNotification.AssertExpectations(t)
}
Regenerating Mocks
When to Regenerate
Run make mock-gen after:
- Adding new methods to an interface
- Changing method signatures
- Removing methods from an interface
- Adding new interfaces to mock
Handling Changes
- Interface method added: Regenerate mocks, update tests
- Interface method removed: Regenerate mocks, remove test expectations
- Method signature changed: Regenerate mocks, update test expectations
# Clean and regenerate all mocks
make mock-clean
make mock-gen
Troubleshooting
Mock Not Found
Problem: undefined: mocks.NewMockUserRepository
Solution:
- Check interface is in
.mockery.yaml - Run
make mock-gen - Check mock file exists in
internal/mocks/
Wrong Package Name
Problem: Mock generated with wrong package
Solution: Check outpkg in .mockery.yaml:
config:
dir: internal/mocks/repository
outpkg: mocks # Must be "mocks"
Interface Not Found
Problem: mockery: error: failed to find interface UserRepository
Solution:
- Verify interface exists and is exported (starts with capital letter)
- Check package path in
.mockery.yamlmatches actual package - Ensure interface name is spelled correctly
Expectation Not Met
Problem: Test fails with "FAIL: Expected function call but did not find it"
Solution:
- Ensure method is actually called in code
- Check argument matchers match actual arguments
- Use
mock.Anythingfor flexible matching during debugging - Add
.Maybe()if call is optional
Best Practices
- Always call
AssertExpectations(t)at the end of tests - Use specific matchers when possible for better test clarity
- Mock only what you need - don't set expectations for unused methods
- Regenerate after interface changes to keep mocks in sync
- Use
Once()explicitly to catch unexpected multiple calls - Group related mocks in the same directory structure
- Keep
.mockery.yamlorganized with clear package groupings
Mock Directory Structure
internal/mocks/
├── repository/
│ ├── mock_UserRepository.go
│ ├── mock_ProductRepository.go
│ └── mock_OrderRepository.go
└── usecase/
├── mock_AuthUsecase.go
├── mock_UserUsecase.go
└── mock_ProductUsecase.go
This structure keeps mocks organized by layer (repository vs usecase) for easy discovery.