Расскажите о вашем опыте мокирования зависимостей в Go. Какие подходы и инструменты вы использовали?

Ответ

Мокирование в Go — это ключевая техника для написания изолированных юнит-тестов. Его основная цель — заменить реальные зависимости (например, базу данных или внешний API) на контролируемые тестовые двойники (моки).

В Go это достигается преимущественно через интерфейсы.

Подходы к мокированию:

  1. Ручное мокирование Простой способ, при котором вы вручную создаете структуру, реализующую нужный интерфейс. Подходит для простых интерфейсов.

    // Интерфейс нашей зависимости
    type UserStorage interface {
        GetUser(id int) (string, error)
    }
    
    // Ручной мок
    type MockUserStorage struct {
        User  string
        Error error
    }
    
    func (m *MockUserStorage) GetUser(id int) (string, error) {
        return m.User, m.Error
    }
    
    // Тест, использующий мок
    func TestUserService(t *testing.T) {
        mockDB := &MockUserStorage{User: "test_user", Error: nil}
        service := NewUserService(mockDB)
        // ... дальнейшее тестирование логики сервиса
    }
  2. Автоматическая генерация моков Для сложных интерфейсов ручное создание моков становится утомительным. Здесь помогают инструменты кодогенерации.

Популярные инструменты:

  • gomock (от Google): Официальный и очень мощный инструмент. Он генерирует моки из интерфейсов с помощью утилиты mockgen. Его особенность — строгий контроль вызовов через controller.

    // Пример теста с gomock
    ctrl := gomock.NewController(t)
    defer ctrl.Finish()
    
    mockDB := NewMockUserStorage(ctrl) // Сгенерированный мок
    mockDB.EXPECT().GetUser(1).Return("test_user", nil).Times(1)
    
    service := NewUserService(mockDB)
    // ...
  • testify/mock: Часть популярной библиотеки testify. Более гибкий и менее строгий, чем gomock. Не требует обязательной кодогенерации (хотя отлично работает в паре с mockery), что делает его проще для старта.

  • mockery: Утилита для генерации моков, которая отлично интегрируется с testify/mock.

Ключевой вывод: Независимо от инструмента, основа хорошего мокирования в Go — это проектирование кода с использованием интерфейсов для отделения бизнес-логики от ее зависимостей.