← Назад к вопросам

Какими пользовался библиотеками для мокирования

1.0 Junior🔥 191 комментариев
#Тестирование

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Библиотеки для мокирования в Go

В Go-разработке я активно использую несколько библиотек для мокирования, каждая из которых решает определенный класс задач. В отличие от языков с динамической типизацией или сложными системами наследования, Go требует особого подхода к мокированию из-за своей статической типизации, явных интерфейсов и минималистичной системы типов.

Основные библиотеки

1. Testify Mock (github.com/stretchr/testify/mock) — наиболее популярное решение для мокирования интерфейсов:

package user_test

import (
    "testing"
    "github.com/stretchr/testify/mock"
    "github.com/stretchr/testify/assert"
)

// Интерфейс, который будем мокировать
type UserRepository interface {
    FindByID(id int) (*User, error)
    Save(user *User) error
}

// Мок-структура
type MockUserRepository struct {
    mock.Mock
}

func (m *MockUserRepository) FindByID(id int) (*User, error) {
    args := m.Called(id)
    return args.Get(0).(*User), args.Error(1)
}

func (m *MockUserRepository) Save(user *User) error {
    args := m.Called(user)
    return args.Error(0)
}

func TestUserService_GetUser(t *testing.T) {
    // Создаем мок
    mockRepo := new(MockUserRepository)
    
    // Настраиваем ожидания
    expectedUser := &User{ID: 1, Name: "John"}
    mockRepo.On("FindByID", 1).Return(expectedUser, nil)
    
    // Используем мок в тесте
    service := NewUserService(mockRepo)
    user, err := service.GetUser(1)
    
    // Проверяем
    assert.NoError(t, err)
    assert.Equal(t, expectedUser, user)
    mockRepo.AssertExpectations(t) // Проверяем, что все ожидания выполнены
}

2. Gomock (go.uber.org/mock/gomock) — официальная библиотека от Uber с генерацией кода:

//go:generate mockgen -destination=mocks/mock_user_repository.go -package=mocks . UserRepository

func TestWithGomock(t *testing.T) {
    ctrl := gomock.NewController(t)
    defer ctrl.Finish()
    
    mockRepo := mocks.NewMockUserRepository(ctrl)
    
    // Устанавливаем ожидания с более богатым API
    mockRepo.EXPECT().
        FindByID(gomock.Eq(1)).
        Return(&User{ID: 1, Name: "John"}, nil).
        Times(1)
    
    // ... тест
}

3. GoMock (github.com/golang/mock) — оригинальная библиотека, предшественница Uber-версии, сейчас менее распространена.

Специализированные инструменты

4. github.com/DATA-DOG/go-sqlmock — для мокирования SQL-запросов:

func TestDatabaseOperation(t *testing.T) {
    db, mock, err := sqlmock.New()
    if err != nil {
        t.Fatal(err)
    }
    defer db.Close()
    
    // Ожидаем конкретный запрос
    rows := sqlmock.NewRows([]string{"id", "name"}).
        AddRow(1, "John").
        AddRow(2, "Jane")
    
    mock.ExpectQuery("SELECT \\* FROM users WHERE active = ?").
        WithArgs(true).
        WillReturnRows(rows)
    
    // Выполняем тестируемый код
    // ...
    
    // Проверяем, что все ожидания выполнены
    if err := mock.ExpectationsWereMet(); err != nil {
        t.Errorf("невыполненные ожидания: %s", err)
    }
}

5. HTTP-мокирование:

  • github.com/h2non/gock — для мокирования HTTP-запросов
  • github.com/jarcoal/httpmock — альтернатива для http.Client

6. Встроенные возможности Go: часто для простых случаев достаточно использовать встроенные фиктивные реализации (fakes) или заглушки (stubs):

type StubUserRepository struct {
    users map[int]*User
    err   error
}

func (s *StubUserRepository) FindByID(id int) (*User, error) {
    if s.err != nil {
        return nil, s.err
    }
    return s.users[id], nil
}

Критерии выбора библиотеки

При выборе библиотеки я руководствуюсь несколькими критериями:

  • Тип тестирования: для unit-тестов часто достаточно testify, для интеграционных — специализированных моков (sqlmock, httpmock)
  • Сложность сценариев: gomock с генерацией кода лучше подходит для сложных сценариев с множеством вызовов
  • Производительность: легковесные заглушки часто работают быстрее полноценных мок-библиотек
  • Читаемость тестов: testify имеет более понятный синтаксис для простых случаев
  • Поддержка сообществом: testify имеет самую широкую adoption в Go-экосистеме

Практические рекомендации

  1. Не злоупотребляйте мокированием — часто лучше использовать реальные зависимости или фейки
  2. Отдавайте предпочтение интерфейсам — мокировать можно только то, что описывается интерфейсом
  3. Используйте кодогенерацию для сложных интерфейсов — это экономит время и уменьшает вероятность ошибок
  4. Комбинируйте подходы — в разных частях проекта могут быть уместны разные инструменты
  5. Следите за поддержкой библиотек — Go постоянно развивается, некоторые библиотеки устаревают

В моей практике Testify Mock покрывает около 70% случаев мокирования, остальное делится между специализированными библиотеками и кастомными реализациями. Важно помнить, что мокирование — это инструмент, и как любой инструмент, его нужно применять там, где это действительно необходимо для изоляции тестируемого кода.