Перейти к содержимому
ГоМастер

Поиск по урокам

Найдите урок, курс, термин или раздел

gomock или testify/mock: чем мокать зависимости в Go

gomock генерирует строгие моки из интерфейсов: неожиданный вызов роняет тест, а аргументы проверяются по контракту — ценой шага кодогенерации. testify/mock — встраиваемая заглушка с On и Return в стиле динамических языков: мок готов за минуты, но дисциплину держите вы.

gomocktestify/mockкурс

Подбор под ваши приоритеты

Двигайте ползунки — насколько для вас важен каждый критерий (0 — не важен, 5 — критично). Счёт пересчитывается на лету: это взвешенное среднее оценок библиотек по критериям, а не бенчмарк.

Неожиданные вызовы и число обращений

Минуты до рабочего мока

Кодогенерация, mockery, поддержка изменений

Что происходит в секции Arrange

Как быстро новичок пишет первый мок

Взвешенный счёт

  • testify/mockваш матч80
  • gomock76

Под ваши веса лидирует testify/mock: быстрым юнит-тестам поверх testify-стека и командам, которым важнее скорость итераций, чем строгий контроль вызовов.

Вопрос «gomock или testify/mock» встаёт, когда в тестах появляется первый сервис с зависимостью: база, внешний API, очередь. Go не даёт магии рантайма для подмены объектов, поэтому моки строят либо генерацией кода из интерфейсов, либо вручную через встраивание. gomock идёт первым путём: утилита mockgen выдаёт строгую заготовку по интерфейсу, а тест описывает ожидания цепочкой EXPECT. testify/mock идёт вторым: структура с встроенным mock.Mock реализует интерфейс и возвращает заготовленные ответы через On и Return. На этой странице сравним оба по одним критериям, покажем один и тот же мок Store рядом и соберём персональную рекомендацию ползунками важности.

gomock — это дисциплина. Сгенерированный мок знает сигнатуры всех методов: аргументы проверяются матчерами, неожиданный вызов роняет тест, а Times фиксирует, сколько раз метод обязан вызваться. Контроллер завязан на *testing.T, поэтому завершение теста само проверяет все ожидания — забытый вызов базы всплывает без дополнительных assert. Расплата — производственный цикл: интерфейс поменялся — перегенерируй мок, иначе компилятор против; в CI добавляется шаг go generate, а новичок должен понять матчеры вроде gomock.Any и gomock.Eq, прежде чем читать тесты вслух.

testify/mock — это скорость. Структура с встроенным mock.Mock, пара методов с m.Called — и мок готов, без инструментов и генерации. Ожидания описываются в тесте: On привязывает аргументы к ответу, Return задаёт результат, AssertExpectations проверяет, что всё обещанное случилось. Стиль знаком каждому, кто писал моки в динамических языках, и логично продолжает testify-стек: assert, require, suites и моки — один набор зависимостей. Расплата — ручная работа: приведение типов из args.Get, контроль неожиданных вызовов и аккуратность в AssertExpectations, потому что без него тест может пройти, даже если мок не вызвали вовсе.

Как читать итог ниже: оценки — усреднение практики команды проекта, а не замеры; ползунки подставляют ваши приоритеты. Грубая граница: большой проект с десятками интерфейсов и требованиями к строгим проверкам — gomock; быстрый юнит-тест поверх testify-стека — testify/mock; зрелая команда может взять mockery, чтобы получить генерацию моков в стиле testify. Оба подхода разобраны в нашем курсе testify: от знакомства с моками до кодогенерации.

Одна и та же задача в каждой библиотеке

Задача-эталон для этого сравнения: посмотрите на разницу в церемониях, типах и обработке ошибок — так выбор ощущается, а не только описывается словами.

Кодогенерация из интерфейсов: строгий контроль вызовов без ручной рутины

package service_test

import (
  "context"
  "testing"

  "go.uber.org/mock/gomock"
)

// мок сгенерирован mockgen из интерфейса Store:
// mockgen -source=store.go -destination=mock_store.go

func TestGetUser(t *testing.T) {
  ctrl := gomock.NewController(t)
  store := NewMockStore(ctrl)

  store.EXPECT().
    GetByID(gomock.Any(), int64(7)).
    Return("Аня", nil).
    Times(1)

  name, err := store.GetByID(context.Background(), 7)
  if err != nil || name != "Аня" {
    t.Fatalf("получили %q, %v", name, err)
  }
}

Сильные и слабые стороны

gomock

Кодогенерация из интерфейсов: строгий контроль вызовов без ручной рутины

  • Строгий контракт: неожиданный вызов роняет тест, Times фиксирует число обращений
  • Компилятор против расхождений: интерфейс поменялся — мок перегенерируется и несоответствия видны сразу
  • Матчеры аргументов: gomock.Eq, gomock.Any и свои предикаты покрывают проверки входа
  • Шаг кодогенерации: mockgen в цепочке сборки, после правки интерфейса мок надо обновить
  • Чужой синтаксис: EXPECT-цепочки и матчеры надо объяснять новичкам
  • История проекта: исходный golang/mock переведён в архив, живёт форк go.uber.org/mock

Берите, если: большим проектам с десятками интерфейсов, где строгие проверки вызовов дороже минут генерации.

testify/mock

Встраиваемая заглушка: On и Return в стиле динамических языков

  • Мок без инструментов: встроил mock.Mock, описал методы с m.Called — и тест готов
  • Один стек с assert и require: зависимости, синтаксис и привычки не делятся между библиотеками
  • Гибкие проверки: On с точными значениями, MatchedBy для предикатов, AssertCalled и AssertNotCalled
  • Дисциплина на вас: без AssertExpectations тест может пройти, а мок так и не вызвать
  • Приведения типов вручную: args.Get возвращает интерфейс, типы вытаскиваются руками
  • Нет генерации из коробки: для десятков интерфейсов методы описываются копипастой

Берите, если: быстрым юнит-тестам поверх testify-стека и командам, которым важнее скорость итераций, чем строгий контроль вызовов.

Оценки по критериям

Шкала 1-5: экспертная оценка команды проекта по практике, а не синтетика.

Сравнение gomock, testify/mock по критериям
Критерийgomocktestify/mock
Строгость проверокНеожиданные вызовы и число обращений
Скорость написанияМинуты до рабочего мока
ИнструментыКодогенерация, mockery, поддержка изменений
Читаемость тестовЧто происходит в секции Arrange
Порог входаКак быстро новичок пишет первый мок

Вердикт: когда что выбирать

Оба инструмента подменяют зависимости в тестах, но ставку делают на разное. gomock ставит на строгость: моки генерируются из интерфейсов, ожидания проверяются автоматически, лишний вызов базы роняет тест — ценой шага кодогенерации и EXPECT-синтаксиса. testify/mock ставит на скорость: заглушка собирается руками за минуты и живёт в одном стеке с assert и require — ценой ручных приведений типов и самодисциплины в AssertExpectations. Крупным проектам ближе gomock, быстрым итерациям — testify/mock, а mockery соединяет генерацию с testify-стилем.

Большой проект с десятками интерфейсов и строгими требованиямиgomockКодогенерация и строгий контроллер: неожиданный вызов роняет тест
Быстрые юнит-тесты поверх testify-стекаtestify/mockOn и Return без инструментов: мок готов за минуты
Нужна генерация моков, но команда живёт в testifymockeryГенерирует заглушки с mock.Mock внутри — скорость testify без копипасты
Критичный сервис: лишние обращения к базе и API недопустимыgomockTimes и строгие ожидания ловят каждый незапланированный вызов

А что выбрали вы?

Кликните по своему выбору — голос сохранится только в вашем браузере (персональные данные никуда не отправляются).

Частые вопросы

Что лучше — gomock или testify/mock?

Опирайтесь на масштаб и требования. Десятки интерфейсов, строгие проверки вызовов, готовность держать генерацию в сборке — gomock: unexpected call роняет тест без дополнительных assert. Быстрые юнит-тесты и единый стек с assert и require — testify/mock: мок собирается руками за минуты. Честный компромисс — mockery: генерация заглушек, которые используют testify/mock внутри.

Почему gomock требует кодогенерацию?

В Go нет рантайм-магии для подмены методов, поэтому строгий мок должен реализовать интерфейс статически — с точными сигнатурами и матчерами аргументов. Писать такое руками для каждого интерфейса дорого, и mockgen делает это по описанию: поменялся интерфейс — перегенерировали мок, а расхождения ловит компилятор. Взамен вы получаете проверки, которых в ручном подходе нет: число вызовов, порядок и неожиданные обращения.

Как проверить аргументы вызова в testify/mock?

Ожидание описывается в On: точные значения вроде int64(7) проверяются на равенство, а MatchedBy принимает предикат для сложных случаев — например, что в структуре заполнены обязательные поля. Факт вызова проверяют AssertCalled и AssertNotCalled с теми же аргументами, а AssertExpectations сверяет всё обещанное. Помните про приведение типов: args.Get возвращает интерфейс, и типы из него достаются вручную.

Что такое mockery и при чём тут testify?

mockery — утилита кодогенерации моков: она берёт интерфейс и выдаёт готовую структуру с встроенным mock.Mock и методами на m.Called. Получается гибрид: дисциплина генерации из gomock-мира и синтаксис On и Return из testify-мира. В нашем курсе testify кодогенерации посвящён отдельный урок — там же показано, как встроить обновление моков в конвейер go generate.

Другие сравнения