Что такое моки (mock-объекты) и какова их роль в тестировании?

Ответ

Моки (mock-объекты) — это объекты-заглушки, которые имитируют поведение реальных зависимостей (например, баз данных, внешних API, файловой системы) в контролируемой среде, такой как unit-тесты.

Основные цели использования моков:

  1. Изоляция: Тестирование компонента (unit) в отрыве от его внешних зависимостей. Это гарантирует, что тест проверяет только логику самого компонента.
  2. Предсказуемость и контроль: Возможность эмулировать любые сценарии поведения зависимости: успешный ответ, различные виды ошибок, задержки, пустые данные и т.д.
  3. Скорость выполнения тестов: Исключение медленных операций, таких как реальные сетевые запросы или работа с диском, что критически важно для CI/CD.
  4. Проверка взаимодействий: Моки позволяют проверить, что тестируемый код вызывает нужные методы зависимости с правильными параметрами и в правильном порядке.

Важно отличать Mocks от Stubs и Fakes:

  • Stub (Стаб): Простая заглушка, которая возвращает заранее заданные данные. Не проверяет взаимодействия.
  • Mock (Мок): «Умная» заглушка, которая не только возвращает данные, но и отслеживает вызовы к себе (сколько раз был вызван метод, с какими аргументами).
  • Fake (Фейк): Более сложная реализация, которая имеет рабочую, но упрощенную бизнес-логику (например, in-memory база данных вместо настоящей).

Пример мока HTTP-клиента в Go:

// Интерфейс, который мы будем мокировать
type HTTPClient interface {
    Do(req *http.Request) (*http.Response, error)
}

// Структура мока
type MockHTTPClient struct {
    DoFunc func(req *http.Request) (*http.Response, error)
}

// Реализация метода интерфейса для мока
func (m *MockHTTPClient) Do(req *http.Request) (*http.Response, error) {
    // Вызываем переданную функцию, чтобы контролировать результат
    return m.DoFunc(req)
}

// Пример использования в тесте
func TestMyService_FetchData(t *testing.T) {
    // Создаем мок-клиент
    mockClient := &MockHTTPClient{
        DoFunc: func(req *http.Request) (*http.Response, error) {
            // Проверяем, что URL правильный
            if req.URL.String() != "http://example.com/data" {
                t.Errorf("unexpected URL: %s", req.URL.String())
            }
            // Возвращаем заранее подготовленный ответ
            jsonResponse := `{"key":"value"}`
            body := ioutil.NopCloser(strings.NewReader(jsonResponse))
            return &http.Response{StatusCode: 200, Body: body}, nil
        },
    }

    // ... здесь код, который использует HTTPClient ...
    // myService := NewMyService(mockClient)
    // result, err := myService.FetchData()
}

Популярные библиотеки для генерации моков в Go: gomock и testify/mock.