Ответ
В Go для тестирования используется встроенный пакет testing и стандартная утилита go test. Мой подход к тестированию включает несколько ключевых практик:
-
Unit-тесты (Модульные тесты): Тестирование отдельных, изолированных частей кода (функций, методов). Это основа пирамиды тестирования.
-
Table-driven тесты (Табличные тесты): Идеальный способ для проверки функции с множеством различных входных данных и ожидаемых результатов. Это делает тесты компактными и легко расширяемыми.
func TestMultiply(t *testing.T) { testCases := []struct { name string a, b int want int }{ {"positive numbers", 2, 3, 6}, {"multiplication by zero", 0, 5, 0}, {"negative number", -2, 4, -8}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { got := Multiply(tc.a, tc.b) if got != tc.want { t.Errorf("Multiply(%d, %d) = %d; want %d", tc.a, tc.b, got, tc.want) } }) } } -
Интеграционные тесты: Проверяют взаимодействие нескольких компонентов системы (например, сервис и база данных). Для их отделения от быстрых unit-тестов я использую билд-теги.
- Файл теста помечается комментарием:
//go:build integration - Запуск производится отдельной командой:
go test -v -tags=integration
- Файл теста помечается комментарием:
-
Моки и Стабы (Mocks & Stubs): Для изоляции тестируемого компонента от его зависимостей (БД, внешние API) я использую моки. Это позволяет тестировать логику компонента, не затрагивая внешние системы. Популярные библиотеки:
testify/mockиgomock. -
Бенчмарки (Benchmarks): Для измерения производительности критически важных участков кода. Используется
testing.B.func BenchmarkMultiply(b *testing.B) { for i := 0; i < b.N; i++ { Multiply(1024, 1024) } } -
Вспомогательные библиотеки: Часто использую
testify/assertиtestify/requireдля более читаемых и удобных проверок вместо стандартныхif got != want.
Ответ 18+ 🔞
Да ты послушай, как у нас в Go с тестами-то обстоят дела! Ну, то есть, вроде всё просто, пакет testing встроенный, go test под рукой, но если подойти с умом, то можно такую пирамиду выстроить, что любая новая фича будет как на ладони, а баги — ну, почти как мухи зимой, блядь.
Вот смотри, основа основ — это unit-тесты, модульные. Берёшь кусочек кода, функцию там какую-нибудь, от всех отгораживаешь и начинаешь его тыкать палкой со всех сторон. Как та обезьяна с гранатой, только наоборот — ты проверяешь, чтобы она не взорвалась. Без этого нихуя, фундамент же.
А чтобы эти тычки палкой не превращались в трёхтомник «Война и мир», есть гениальная штука — table-driven тесты. Это ж просто песня, а не подход! Всю проверятельную хуйню в одну таблицу пишешь, и поехали.
func TestMultiply(t *testing.T) {
testCases := []struct {
name string
a, b int
want int
}{
{"positive numbers", 2, 3, 6},
{"multiplication by zero", 0, 5, 0},
{"negative number", -2, 4, -8},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got := Multiply(tc.a, tc.b)
if got != tc.want {
t.Errorf("Multiply(%d, %d) = %d; want %d", tc.a, tc.b, got, tc.want)
}
})
}
}
Видал? Красота! Добавил новую строку в таблицу — и новый тест готов, ёпта. Никакого копипаста, всё чисто.
Дальше — интеграционные тесты. Это когда твои модули уже друг с другом общаться начинают. Тут уже по-взрослому: база данных, внешние API, всякая такая срань. Чтобы они не тормозили все твои быстрые юнит-тесты, их надо отделять. Берёшь и пишешь в начале файла //go:build integration. И запускаешь их отдельной командой: go test -v -tags=integration. Как будто у тебя два разных войска: одно — быстрые десантники (юниты), а второе — тяжёлая артиллерия с обозом (интеграционные).
А теперь, внимание, самый цимес — моки, блядь! Ну представь, твой код звонит куда-то в облако, а облако сегодня тупит, или база данных легла. И что, из-за этого все твои тесты должны падать? Да хуй там! Мы создаём муляж, этакую мартышку-подставку, которая делает вид, что она — грозное внешнее API. Библиотеки testify/mock или gomock — это наши главные постановщики таких кукольных театров. Подсовываем компоненту поддельную зависимость и смотрим, как он с ней взаимодействует. Чистая магия, ебать мои старые костыли!
Ну и куда же без бенчмарков? Это когда уже не «работает ли», а «как быстро, сука, работает». Написал функцию, которая сортирует миллион чисел, а она делает это за час? Пиздец, не годится. Берёшь testing.B и начинаешь гонять.
func BenchmarkMultiply(b *testing.B) {
for i := 0; i < b.N; i++ {
Multiply(1024, 1024)
}
}
Запустил — и сразу видно, где бутылочное горлышко. Красота!
А, и ещё лайфхак: не мучайся со стандартными if got != want. Возьми библиотечку testify/assert — и твои проверки станут читаться как стихи, ей-богу. Вместо трёх строк кода — одна понятная строчка: assert.Equal(t, 42, result). Удобство — овердохуища!
Вот так-то, друг. Без всего этого — ты просто пишешь код в темноте. А с этим — у тебя как будто прожектор и инструкция по сборке одновременно.