Серия 4 одиночного теста Go — пробный тест интерфейса.
Серия 4 одиночного теста Go — пробный тест интерфейса.

Это третье руководство из серии руководств по модульному тестированию языка Go с нуля. В нем рассказывается, как использовать макетный интерфейс инструментов gomock и gostub и заглушки в модульном тестировании.

В предыдущей статье «Пройдите одиночный тест серии 3 — тест базы данных»,Мы рассмотрели, как использоватьgo-sqlmockиminiredisИнструменты для тестирования баз данных。 Помимо внешних зависимостей, таких как сети и базы данных, при разработке мы часто используем различные типы интерфейсов. В этой статье будет приведен пример, демонстрирующий, как имитировать типы интерфейсов и как выполнять укладку при написании модульных тестов.

Пример кода «Пройдите одиночный тест от нуля до скользкой серии» загружен на Github.,Нажмите👉🏻https://github.com/go-quiz/golang-unit-test-demo Посмотреть полный исходный код.

gomock

gomockдаGoОфициальная система тестирования,Его можно легко использовать во встроенном пакете тестирования или в других средах. Мы используем его, чтобы имитировать эти типы интерфейсов в коде.,Легко писать тестирование。

Установить мокген

Интернет-библиотеки с открытым исходным кодом обновляются и повторяются относительно быстро.,Рекомендуется непосредственно проверить официальную документацию.:https://github.com/golang/mock

Сначала вам нужно убедиться, что ваш$GOPATH/binУже добавлено в переменные среды。

Goномер версии<1.16час:

Язык кода:javascript
копировать
GO111MODULE=on go get github.com/golang/mock/mockgen@v1.6.0

GoВерсия>=1.16час:

Язык кода:javascript
копировать
go install github.com/golang/mock/mockgen@v1.6.0

Если установка находится в вашем конвейере CI, вам потребуется соответствующая версия установки, соответствующая вашей среде CI.

Запустить макет

mockgen Существует два режима работы: режим источника и режим отражения.

Режим исходного кода

Режим исходного код на основе исходного файла макета интерфейса. Это делается с помощью -source Флаг включен. Другие флаги, которые могут быть полезны в этом режиме: -imports и -aux_files

Например:

Язык кода:javascript
копировать
mockgen -source=foo.go [other options]
отражающий режим

отражающий режимMock интерфейс путем создания программ, которые используют отражение для понимания интерфейса. Это включается путем передачи двух параметров без флагов: пути импорта и списка символов, разделенных запятыми. Можно использовать "." относится к пакету текущего пути.

Например:

Язык кода:javascript
копировать
mockgen database/sql/driver Conn,Driver

# Convenient for `go:generate`.
mockgen . Conn,Driver

flags

mockgen Команда используется для создания исходного кода фиктивного класса для данного исходного файла Go, содержащего имитируемый интерфейс. Он поддерживает следующие флаги:

  • -source:Содержать доmockизинтерфейсиздокумент。
  • -destination:генерироватьизисточниккодписатьиздокумент。Если это не установлено,Код будет выведен на стандартный вывод.
  • -package:用于генерироватьизмоделирование类источниккодизимя пакета。Если это не установленоимя пакета默认在原имя пакета前添加mock_префикс。
  • -imports:在генерироватьизисточниккодиспользуется виз Явный список импорта。Значениеfoo=bar/bazформаизчерез запятуюизсписок элементов,где bar/baz — импортируемый пакет,foo — это идентификатор, который будет использоваться для пакета в сгенерированном исходном коде.
  • -aux_files:Нужна ссылка для решенияиздополнительныйдокументсписок,Например, встроенный интерфейс, определенный в разных файлах. Указанное значение должно представлять собой список элементов, разделенных запятыми, в форме foo=bar/baz.go.,где bar/baz.go — исходный файл,fooда-sourceдокументиспользоватьиздокументизимя пакета。
  • -build_flags:(толькоотражающий режим) передать флаг, чтобы перейти к дословному переводу build
  • -mock_names:генерироватьизмоделированиеиз Настроитьимясписок。Это указанодляодинчерез запятуюизсписок элементов,ФормаRepository = MockSensorRepository,Endpoint=MockSensorEndpointRepositoryдаинтерфейсимя,mockSensorrepositoryданеобходимыйизmockимя(mockфабричный методиmockРегистратор будетmockимя)。если один изинтерфейс没有指定Настроитьимя,Будет использовано соглашение об именах по умолчанию.
  • -self_package:генерироватьизкодиз Полный путь импорта пакета。используйте этоflagизглазиздапытаясь сдержать себяиз包Приходить防止генерироватькодсерединаиз Круговой импорт。еслиmockиз Пакет установлендляэтоизвход(в целомдаосновной вход),и вывод — stdio,Тогда Mockgen не может обнаружить окончательный выходной пакет.,Это произойдет. Установка этого флага сообщит макету о том, какая импортная нестабильность.
  • -copyright_file:用于将авторское право标头添加到генерироватьизисточниккодсерединаизавторское праводокумент
  • -debug_parser:только Распечатать результаты парсера
  • -exec_only:(отражающий режим) Если установлено, выполнить эту программу отражения
  • -prog_only:(отражающий режим) только генерирует программу отражения, записывает ее в стандартный вывод и завершает работу.
  • -write_package_comment:еслидляtrue,затем напишите комментарий к документации пакета (годок) (по умолчанию верно).

Создать макет

Здесь мы возьмем операции с базой данных, часто используемые в повседневной разработке, в качестве примера, чтобы объяснить, как использовать gomock для модульного тестирования макетного интерфейса.

Предположим, что бизнес-код для запроса базы данных MySQL выглядит следующим образом:,вDBдаодин Настроитьизинтерфейстип:

Язык кода:javascript
копировать
// db.go

// DB данныеинтерфейс
type DB interface {
	Get(key string)(int, error)
	Add(key string, value int) error
}


// GetFromDB Функция запроса данных из БД на основе ключа
func GetFromDB(db DB, key string) int {
	if v, err := db.Get(key);err == nil{
		return v
	}
	return -1
}

Мы сейчас собираемсяGetFromDBНаписание функций Модульное обновляем код, но не можем в Модульном Процесс тестирования для подключения к реальной базе данных, на этот раз вам понадобится макет DBэтотинтерфейсоблегчить Модульное тестирование。

Используйте вышеупомянутое mockgen инструмент Приходитьдлягенерировать相应изmockкод。Выполнив следующие действияиз Заказ,Мы можем создать его в рамках текущего проектаmocksпапка,Внутри хранится одинdb_mock.goдокумент。

Язык кода:javascript
копировать
 mockgen -source=db.go -destination=mocks/db_mock.go -package=mocks

db_mock.goдокументсерединаиз СодержаниедаmockСвязанныйинтерфейсизкод Понятно。

Обычно нам не нужно его редактировать.,Просто используйте их установленным образом в Модульном тестировании. Например,мы пишемTestGetFromDB Функция следующая:

Язык кода:javascript
копировать
// db_test.go

func TestGetFromDB(t *testing.T) {
	// Создайте контроллер gomock для записи информации о последующих операциях.
	ctrl := gomock.NewController(t)
	// утверждение Ожидаемые методы выполняются
	// Больше нет необходимости вручную вызывать этот метод в одном тесте Go1.14+.
	defer ctrl.Finish()
	// Вызовите метод NewMockDB в коде, сгенерированном макетом.
	// Здесь «mocks» — это имя пакета, указанное при генерации кода.
	m := mocks.NewMockDB(ctrl)
	// заглушка
	// Если в функцию Get передан параметр liwenzhou.com, возвращается 1nil.
	m.
		EXPECT().
		Get(gomock.Eq("liwenzhou.com")). // параметр
		Return(1, nil).                  // возвращаемое значение
		Times(1)                         // Количество звонков

	// При вызове функции GetFromDB передайте приведенный выше фиктивный объект m.
	if v := GetFromDB(m, "liwenzhou.com"); v != 1 {
		t.Fatal()
	}
}

заглушка

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

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

В приведенном выше коде используется свая,когда вошелGetфункцияизпараметрдляliwenzhou.comчас Просто вернись1, nilизвозвращаемое значение。

gomockПоддержкапараметр、возвращаемое значение、Количество звонков、Последовательность звонков и другие проводили свайные работы.

параметр

Связанное использование параметра включает: - gomock.Eq(value): представляет параметр, эквивалентный значению. - gomock.Not(value): параметр, представляющий значение, не являющееся значением. - gomock.Any(): параметр, представляющий любое значение. - gomock.Nil(): параметр, представляющий нулевое значение. - SetArg(n, значение): установите значение n-го (начиная с 0) параметра, обычно используемого для указателей или срезов.

Конкретные примеры заключаются в следующем:

Язык кода:javascript
копировать
m.EXPECT().Get(gomock.Not("q1mi")).Return(10, nil)
m.EXPECT().Get(gomock.Any()).Return(20, nil)
m.EXPECT().Get(gomock.Nil()).Return(-1, nil)

Давайте поговорим об этом отдельно здесьSetArgиз Применимые сценарии,Предположим, у вас есть программа, которую нужно имитировать следующим образом:

Язык кода:javascript
копировать
type YourInterface {
  SetValue(arg *int)
}

в это время,штабелированиеизчас候就可以использоватьSetArgизменитьпараметризценить。

Язык кода:javascript
копировать
m.EXPECT().SetValue(gomock.Any()).SetArg(0, 7)  // Установите первый параметр SetValue равным 7.
возвращаемое значение

Обычаи, связанные с возвращаемым значением в gomock, следующие:

  • Return(): возвращает указанное значение.
  • Do (func): выполнить операцию, игнорировать возвращаемое значение.
  • DoAndReturn(func): выполнить и вернуть указанное значение.

Например:

Язык кода:javascript
копировать
m.EXPECT().Get(gomock.Any()).Return(20, nil)
m.EXPECT().Get(gomock.Any()).Do(func(key string) {
	t.Logf("input key is %v\n", key)
})
m.EXPECT().Get(gomock.Any()).DoAndReturn(func(key string)(int, error) {
	t.Logf("input key is %v\n", key)
	return 10, nil
})
Количество звонков

Методы, имитируемые с помощью инструмента gomock, будут вызываться ожидаемое количество раз. По умолчанию каждый метод можно вызывать только один раз.

Язык кода:javascript
копировать
m.
	EXPECT().
	Get(gomock.Eq("liwenzhou.com")). // параметр
	Return(1, nil).                  // возвращаемое значение
	Times(1)                         // Установите метод Get для ожидания количества звонковдля1

// При вызове функции GetFromDB передайте приведенный выше фиктивный объект m.
if v := GetFromDB(m, "liwenzhou.com"); v != 1 {
	t.Fatal()
}
// При повторном вызове метода Get из макета выше он не удовлетворяет требованию количества. звонков - 1 ожидание
if v := GetFromDB(m, "liwenzhou.com"); v != 1 {
	t.Fatal()
}

gomock предоставляет нам следующий метод для установки ожидаемого количества вызовов.

  • Times() утверждение Mock Сколько раз вызывался метод.
  • MaxTimes() Максимальное количество раз.
  • MinTimes() Минимальное количество раз.
  • AnyTimes() любое количество раз (в том числе 0 Второсортный).
Последовательность звонков

gomockОн также поддерживает использованиеInOrderСпецификация методаmockметодиз Последовательность звонков:

Язык кода:javascript
копировать
// Укажите заказ
gomock.InOrder(
	m.EXPECT().Get("1"),
	m.EXPECT().Get("2"),
	m.EXPECT().Get("3"),
)

// Вызывается последовательно
GetFromDB(m, "1")
GetFromDB(m, "2")
GetFromDB(m, "3")

Также известныйизGoтестовая библиотекаtestifyглаз前также提供类似изmockинструмент—testify/mockиmockery

GoStub

GoStubтакжедаодин Модульное Инструмент накопления в тестировании поддерживает накопление глобальных переменных, функций и т. д.

Однако лично я считаю, что это не очень удобно для объединения функций. Обычно я использую его только для объединения глобальных переменных в модульных тестах.

Установить

Язык кода:javascript
копировать
go get github.com/prashantv/gostub

Пример использования

Здесь мы используем пример кода из официальной документации, чтобы продемонстрировать, как использовать gostub для заглушки глобальных переменных.

Язык кода:javascript
копировать
// app.go 

var (
	configFile = "config.json"
	maxNum = 10
)


func GetConfig() ([]byte, error) {
	return ioutil.ReadFile(configFile)
}


func ShowNumber()int{
	// ...
	return maxNum
}

Приведенный выше код определяет две глобальные переменные и две функции, использующие глобальные переменные. Теперь мы напишем Модульное тестирование для этих двух функций.

Язык кода:javascript
копировать
// app_test.go


import (
	"github.com/prashantv/gostub"
	"testing"
)

func TestGetConfig(t *testing.T) {
	// Сложите глобальную переменную configFile и назначьте ей указанный файл.
	stubs := gostub.Stub(&configFile, "./test.toml")
	defer stubs.Reset()  // Сброс после тестирования
	// Ниже приведен код теста
	data, err := GetConfig()
	if err != nil {
		t.Fatal()
	}
	// Содержимое возвращаемых данных — это содержимое файла /tmp/test.config, указанного выше.
	t.Logf("data:%s\n", data)
}

func TestShowNumber(t *testing.T) {
	stubs := gostub.Stub(&maxNum, 20)
	defer stubs.Reset()
	// Вот тестовый код
	res := ShowNumber()
	if res != 20 {
		t.Fatal()
	}
}

Выполните модульный тест и просмотрите результаты:

Язык кода:javascript
копировать
❯ go test -v
=== RUN   TestGetConfig
    app_test.go:18: data:blog="liwenzhou.com"
--- PASS: TestGetConfig (0.00s)
=== RUN   TestShowNumber
--- PASS: TestShowNumber (0.00s)
PASS
ok      golang-unit-test-demo/gostub_demo       0.012s

Из приведенного выше примера мы видим, что в Модульном тестированиеиспользуется вgostubможет быть очень удобноиз对全局变量进行штабелирование,Приведите его к нашему ожидаемому значению для тестирования.

Подвести итог

Написание кода для ежедневной работы Модульное тестированиечас Как бороться скодсерединаизинтерфейстипда Очень частоизвопрос,В этой статье объясняется, как использоватьgomockmockСвязанныйинтерфейси Как использоватьgostubинструмент对全局变量进行штабелирование。

в следующей статье,мы пойдем дальше,Подробная информация о том, как написать Модульное тестированиечасиспользовать更全能изштабелированиеинструмент——monkey

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose