Что такое мок-объекты в тестировании и для чего они нужны?
Что проверяют
Понимаете ли вы, зачем изолировать проверяемый код от зависимостей, и различаете ли виды тестовых дублёров. В Go к этому добавляется вопрос про интерфейсы: моки тут не генерируются из классов, а вырастают из того, как вы спроектировали границы пакета.
Как отвечать
Дублёр это подстановка вместо настоящей зависимости. Заглушка просто возвращает заданный ответ, фейк это упрощённая рабочая реализация, например хранилище в памяти, а мок в строгом смысле ещё и проверяет ожидания: что вызвали, с какими аргументами и сколько раз. Нужны они, чтобы тест проверял одну единицу логики, шёл быстро и давал воспроизводимый результат, включая ветки ошибок, которые иначе не вызвать.
В Go интерфейс объявляется на стороне потребителя и держится узким, тогда подмена пишется руками за несколько строк.
type userStore interface {
ByID(ctx context.Context, id int64) (User, error)
}
type stubStore struct {
user User
err error
calls int
}
func (s *stubStore) ByID(context.Context, int64) (User, error) {
s.calls++
return s.user, s.err
}Генераторы моков окупаются на широких интерфейсах и на проверке строгих ожиданий, но для двух методов ручной дублёр читается лучше.
Где спотыкаются
Подменяют всё подряд, включая собственную логику. Тест начинает описывать реализацию, и любой безобидный рефакторинг его ломает, хотя поведение не изменилось. Признак перебора: в тесте настроек больше, чем проверок.
Путают роли и называют моком заглушку, а потом не проверяют взаимодействие там, где вопрос именно в нём. Отправлено ли письмо ровно один раз при повторе запроса, ушёл ли платёж дважды: это проверяется счётчиком вызовов и аргументами, а не возвращённым значением.
Подменяют то, что дешевле поднять по-настоящему. Для HTTP-клиента есть тестовый сервер из стандартной библиотеки, для базы поднимают контейнер в интеграционном тесте, а мок поверх драйвера прячет ровно те ошибки запросов и миграций, ради которых тест и писали.