Ответ
Мок (Mock) — это объект-двойник, который имитирует поведение реальной зависимости (например, базы данных, внешнего API, файловой системы) в изолированной тестовой среде. Основная цель — проверить, как тестируемый код взаимодействует с этой зависимостью.
Ключевые задачи моков:
- Изоляция: Тестировать компонент, не затрагивая его зависимости. Это делает тесты предсказуемыми и быстрыми.
- Симуляция состояний: Воспроизводить сценарии, которые сложно или невозможно получить с реальными зависимостями (например, ошибка сети, перегрузка базы данных, специфический ответ от API).
- Верификация поведения: Убедиться, что тестируемый код вызывает нужные методы зависимости с правильными параметрами и в верном порядке.
Моки, Стабы, Фейки: в чем разница?
- Стаб (Stub): Простая заглушка, которая возвращает заранее подготовленные данные. Не проверяет, как её вызывали.
- Мок (Mock): Объект с заранее заданными ожиданиями (expectations). Тест провалится, если вызовы не будут соответствовать этим ожиданиям (не тот метод, не те аргументы).
- Фейк (Fake): Упрощенная, но рабочая реализация зависимости. Например, in-memory база данных вместо реальной PostgreSQL.
Пример с gomock
в Go:
Предположим, у нас есть интерфейс для работы с базой данных. mockgen
сгенерирует для него мок.
// Интерфейс, который мы будем мокировать
type DB interface {
Get(key string) (string, error)
}
// Тест, использующий сгенерированный мок
func TestService(t *testing.T) {
// 1. Инициализация контроллера для управления моками
ctrl := gomock.NewController(t)
defer ctrl.Finish()
// 2. Создание экземпляра мока
mockDB := NewMockDB(ctrl) // NewMockDB генерируется mockgen
// 3. Установка ожидания: мы ожидаем вызов Get("test")
// и указываем, что он должен вернуть "value" и nil
mockDB.EXPECT().Get("test").Return("value", nil)
// 4. Внедрение мока в тестируемый сервис
service := NewService(mockDB)
result, _ := service.DoSomething("test")
// 5. Проверка результата
assert.Equal(t, "value_processed", result)
}
Плюсы:
- Скорость: Тесты выполняются мгновенно, так как нет реальных сетевых вызовов или обращений к диску.
- Надежность: Тесты не зависят от внешних систем и их доступности.
- Полнота покрытия: Позволяют легко тестировать обработку ошибок и другие крайние случаи.
Минусы:
- Хрупкость: Моки тесно связаны с интерфейсом зависимости. При изменении интерфейса придется обновлять все моки и тесты.
- Риск рассинхронизации: Мок может имитировать поведение, которое уже не соответствует реальной системе, что приведет к ложно-положительным тестам.