Интерфейсы в Go
Теория: Интерфейсы для моков
Во время разработки код часто зависит от внешних источников: базы данных, сетевых сервисов, файловых систем. Такие зависимости сложно протестировать напрямую — они медленные, нестабильные и требуют настройки. Чтобы изолировать бизнес-логику и проверить её поведение, используют моки — поддельные реализации интерфейсов. Они ведут себя как настоящие зависимости, но работают локально, предсказуемо и быстро.
Чтобы заменить реальную зависимость на мок, в коде должна быть абстракция. В Go такой абстракцией служит интерфейс. Если функция зависит не от конкретного типа, а от интерфейса, можно передать любую реализацию — в том числе тестовую заглушку. Это позволяет проверить логику независимо от внешнего окружения. Допустим, есть функция, которая сохраняет пользователя в хранилище. В простейшем варианте она может выглядеть так:
Тестировать такую функцию неудобно. Она жёстко привязана к базе данных. Чтобы упростить тест, достаточно выделить интерфейс:
Теперь в тестах можно передать подмену:
И сам тест:
Такой способ называют ручным мокированием. Он удобен, когда интерфейс небольшой и содержит один-два метода. В более сложных случаях код заглушек становится громоздким. Для решения таких проблем создали библиотеки, такие как Mockery.
Mockery автоматически генерирует моки на основе интерфейсов. Интерфейс описывается один раз, после чего команда генерации создаёт подменную реализацию. Она включает инструменты для задания ожиданий вызовов, аргументов и проверки порядка/кратности. Чтобы начать, установите генератор:
Затем сгенерируйте мок (пример для интерфейса Storage), используя типобезопасный expecter-API, максимально похожий на EXPECT() из GoMock:
В тестах отдельный контроллер не нужен — конструктор мока привязывается к *testing.T и сам повесит AssertExpectations через t.Cleanup(). Пример:
Метод EXPECT() задаёт ожидаемые вызовы и аргументы. Если ожидание не будет выполнено (вызвано с другими аргументами, не было вызвано вовсе или вызвано неверное число раз), тест упадёт на проверке ожиданий.
Альтернатива: без --with-expecter можно использовать стиль Testify:
Но expecter-API ближе к привычному синтаксису из GoMock.
Автоматическое мокирование особенно полезно в больших проектах, где интерфейсы содержат десятки методов: оно избавляет от рутины, делает тесты чище и снижает риск ошибок. Благодаря структурной типизации Go достаточно, чтобы тип соответствовал методам интерфейса, — и его можно подменить. Поэтому интерфейсы в Go критичны для тестируемого кода.


