Ответ
И моки, и стабы являются тестовыми двойниками (test doubles), которые используются для изоляции тестируемого компонента от его зависимостей. Ключевое различие заключается в цели их использования и способе проверки.
Стабы (Stubs)
Цель: Предоставить заранее заготовленные данные для тестируемого кода. Стабы используются для проверки состояния (state verification).
Вы проверяете, что ваш код, получив определённые данные от зависимости, возвращает правильный результат или меняет своё состояние корректным образом.
Пример: Мы тестируем функцию, которая получает пользователя из базы данных и форматирует его имя. Нам не важна реализация БД, нам просто нужно, чтобы она вернула пользователя.
// Зависимость, которую мы заменяем стабом
// type UserProvider interface {
// GetUser(id int) (*User, error)
// }
// Простой стаб, который всегда возвращает одного и того же пользователя
type DBStub struct{}
func (d *DBStub) GetUser(id int) (*User, error) {
// Возвращаем заранее определённые данные
return &User{ID: id, Name: "Test User"}, nil
}
Моки (Mocks)
Цель: Проверить, что тестируемый код правильно взаимодействует со своими зависимостями. Моки используются для проверки поведения (behavior verification).
Вы проверяете, что ваш код вызвал нужные методы зависимости, с правильными аргументами и в правильном порядке.
Пример: Мы тестируем функцию, которая при регистрации пользователя должна не только создать его в БД, но и отправить email. Мы хотим убедиться, что метод отправки email был вызван ровно 1 раз с правильным адресом.
// Используем популярную библиотеку testify/mock
import "github.com/stretchr/testify/mock"
// Мок-объект для сервиса уведомлений
type NotificationServiceMock struct {
mock.Mock
}
func (m *NotificationServiceMock) SendWelcomeEmail(email string) error {
// Регистрируем вызов и его аргументы
args := m.Called(email)
// Возвращаем то, что было настроено в тесте
return args.Error(0)
}
// В тесте:
mockService := new(NotificationServiceMock)
// Ожидаем, что метод SendWelcomeEmail будет вызван 1 раз с аргументом "test@example.com"
mockService.On("SendWelcomeEmail", "test@example.com").Return(nil).Once()
// ... вызываем тестируемую функцию ...
// Проверяем, что все ожидания оправдались
mockService.AssertExpectations(t)
Ключевые отличия в таблице:
| Характеристика | Стаб (Stub) | Мок (Mock) |
|---|---|---|
| Основная цель | Подменить зависимость для предоставления данных | Проверить правильность взаимодействия с зависимостью |
| Что проверяет | Конечное состояние системы (State Verification) | Последовательность и параметры вызовов (Behavior Verification) |
| Сложность | Обычно проще, реализуется вручную | Часто требует библиотек (testify/mock), сложнее в настройке |
Итог: Используйте стабы, когда вам нужно просто "заткнуть" зависимость данными. Используйте моки, когда вам критически важно проверить, как именно ваш код взаимодействует с зависимостью.
Ответ 18+ 🔞
А, ну вот, опять про эти ваши моки и стабы, блядь! Слушай, я тебе сейчас так разложу, что ты, сука, до конца жизни разницу эту чувствовать будешь, как зубную боль. Это ж элементарно, ёпта!
Представь себе, ты тестируешь свой код, а он там, сука, с какой-то базой данных общается или письма рассылает. И тебе надо, чтобы эта зависимость не мешала, не палила контору на проде, пока ты экспериментируешь. Вот для этого и придумали этих двойников — test doubles, блядь.
Так вот, стаб — это как подсадная утка, понимаешь? Ты её подсовываешь, чтобы она просто молча, без вопросов, отдала тебе заранее приготовленные данные. Проверяешь ты в итоге состояние своей системы. То есть: «Получил я от этой заглушки пользователя с именем «Вася Пупкин» — правильно ли моя функция его отформатировала?». Всё, больше от стаба нихуя не требуется. Он тупо возвращает то, что ты ему приказал, и идёт на хуй.
// Вот смотри, тупой как пробка стаб. Ему сказали — верни юзера.
type DBStub struct{}
func (d *DBStub) GetUser(id int) (*User, error) {
// Никакой логики, блядь! Просто возвращаем фикстуру и всё.
return &User{ID: id, Name: "Test User"}, nil
}
А теперь, блядь, мок — это уже не просто заглушка, это, сука, стукач! Инспектор! Его задача — следить за твоим кодом и потом доложить, правильно ли тот общался с внешним миром. Проверяешь ты уже поведение. То есть: «А вызывал ли мой метод RegisterUser этот ебучий сервис нотификаций, чтобы отправить приветственное письмо? И вызвал ровно один раз? И с правильным email-адресом, блядь?».
// А это уже мок — хитрая жопа с ушами.
import "github.com/stretchr/testify/mock"
type NotificationServiceMock struct {
mock.Mock // Вот эта штука всё запоминает, блядь.
}
func (m *NotificationServiceMock) SendWelcomeEmail(email string) error {
// О, смотри! Он фиксирует вызов: кто, куда и с чем приходил.
args := m.Called(email)
return args.Error(0)
}
// А в тесте ты ему инструкцию пишешь, как стучать:
mockService := new(NotificationServiceMock)
// «Жду, что метод SendWelcomeEmail вызовут один раз с таким-то email», — говоришь ты.
mockService.On("SendWelcomeEmail", "test@example.com").Return(nil).Once()
// ... запускаешь свой код ...
// И потом спрашиваешь: «Ну что, стукач, всё как я велел?»
mockService.AssertExpectations(t) // Если не так — пиздец, тест упал.
Короче, если совсем просто, то:
- Стаб — это тупой курьер, который принёс тебе коробку с данными. Тебе важно, что в коробке.
- Мок — это параноидальный контролёр на проходной. Ему важно, кто, куда и с каким лицом заходил, и совпадает ли это с его списком.
Используй стабы, когда тебе просто надо заткнуть дыру и дать коду данные. А моки — когда тебе жизненно важно знать, что твой код не просто получил данные, а ещё и правильно, блядь, постучался в нужные двери. Вот и вся философия, в рот меня чих-пых!