Моки (mocks) — это специальные объекты, которые имитируют поведение реальных зависимостей в тестах, позволяя изолировать тестируемый код и контролировать его окружение.
Рассмотрим пример с моком хранилища:
// Реальный интерфейс хранилища
type UserRepository interface {
GetUser(id int) (*User, error)
SaveUser(user *User) error
}
// Реализация мока
type MockUserRepository struct {
GetUserFunc func(id int) (*User, error)
SaveUserFunc func(user *User) error
GetUserCalled bool
SaveUserCalled bool
}
func (m *MockUserRepository) GetUser(id int) (*User, error) {
m.GetUserCalled = true
return m.GetUserFunc(id)
}
func (m *MockUserRepository) SaveUser(user *User) error {
m.SaveUserCalled = true
return m.SaveUserFunc(user)
}
// Использование в тесте
func TestUserService(t *testing.T) {
mockRepo := &MockUserRepository{
GetUserFunc: func(id int) (*User, error) {
return &User{ID: id, Name: "Test User"}, nil
},
}
service := NewUserService(mockRepo)
user, err := service.GetUser(123)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if user.Name != "Test User" {
t.Errorf("unexpected user name: %s", user.Name)
}
if !mockRepo.GetUserCalled {
t.Error("GetUser was not called")
}
}
// Генерация мока: mockgen -source=repository.go -destination=repository_mock.go
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockRepo := NewMockUserRepository(ctrl)
mockRepo.EXPECT().
GetUser(gomock.Eq(123)).
Return(&User{Name: "John"}, nil)
service := NewUserService(mockRepo)
// ... вызов тестируемого кода
mockRepo := new(MockUserRepository)
mockRepo.On("GetUser", 123).Return(&User{Name: "John"}, nil)
service := NewUserService(mockRepo)
// ... вызов тестируемого кода
mockRepo.AssertExpectations(t) // Проверка всех ожиданий
Пример фейка:
type FakeUserRepository struct {
users map[int]*User
}
func (f *FakeUserRepository) GetUser(id int) (*User, error) {
return f.users[id], nil
}
Моки в Go — это мощный инструмент для:
Используйте моки осознанно, сочетая их с другими видами тестовых двойников, чтобы создавать надежные и поддерживаемые тесты.