# Тестування в Go у 2026: модульні тести, моки та питання технічних співбесід > Опануйте тестування в Go зі стандартною бібліотекою, табличними тестами, моками та патернами, які очікують на співбесідах. Практичні приклади з testify, gomock та пакетом testing. - Published: 2026-08-24 - Updated: 2026-08-24 - Author: Anthony Fillion-Maillet - Tags: go, testing, unit-tests, mocks, interview - Reading time: 9 min --- Тестування в Go базується на стандартному пакеті `testing`, який поставляється з кожною інсталяцією Go і не потребує жодних зовнішніх залежностей. На відміну від фреймворків в інших мовах, підхід Go надає перевагу простоті: тестові файли знаходяться поруч з продакшн кодом, тестові функції дотримуються конвенції іменування, а команда `go test` обробляє виявлення та виконання тестів. > **На що звертають увагу інтерв'юери** > > На кандидатів, які пишуть табличні тести, використовують інтерфейси для впровадження залежностей і розуміють, коли мокування додає цінність, а коли лише зайву складність. ## Написання модульних тестів з пакетом testing Кожен тестовий файл закінчується на `_test.go` і знаходиться в тому ж пакеті, що й код, який тестується. Тестові функції починаються з `Test`, за яким йде назва з великої літери. Параметр `*testing.T` надає методи для звітування про помилки. ```go // calculator_test.go package calculator import "testing" func TestAdd(t *testing.T) { result := Add(2, 3) if result != 5 { t.Errorf("Add(2, 3) = %d; want 5", result) } } ``` Виконання `go test ./...` запускає всі тести в поточному модулі. Прапорець `-v` показує назви окремих тестів та їх статус pass/fail. ## Табличні тести: стандарт Go Табличні тести зменшують дублювання і роблять додавання нових випадків тривіальним. Кожен рядок у таблиці представляє один сценарій з вхідними даними та очікуваними результатами. Цей патерн зустрічається в [стандартній бібліотеці Go](https://cs.opensource.google/go/go/+/refs/tags/go1.23.1:src/strings/strings_test.go) та в продакшн кодових базах по всій індустрії. ```go // validator_test.go package validator import "testing" func TestValidateEmail(t *testing.T) { tests := []struct { name string email string wantErr bool }{ {"valid email", "user@example.com", false}, {"missing at sign", "userexample.com", true}, {"empty string", "", true}, {"unicode local part", "user@例え.jp", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := ValidateEmail(tt.email) if (err != nil) != tt.wantErr { t.Errorf("ValidateEmail(%q) error = %v, wantErr %v", tt.email, err, tt.wantErr) } }) } } ``` Метод `t.Run` створює підтести, які виконуються незалежно. Невдалі підтести повідомляють, який конкретний випадок провалився, не зупиняючи інші випадки. ## Впровадження залежностей через інтерфейси Інтерфейси Go дозволяють тестувати код, який залежить від зовнішніх систем: баз даних, HTTP клієнтів, файлових систем. Визначення мінімального інтерфейсу в місці використання дозволяє замінювати реальні імплементації на тестові дублери. ```go // service.go package order // Repository defines the data access methods this service needs type Repository interface { FindByID(id string) (*Order, error) Save(order *Order) error } // Service handles order business logic type Service struct { repo Repository } // NewService creates a service with the given repository func NewService(repo Repository) *Service { return &Service{repo: repo} } // Process validates and saves an order func (s *Service) Process(order *Order) error { if order.Total <= 0 { return ErrInvalidTotal } return s.repo.Save(order) } ``` `Service` приймає будь-який тип, що задовольняє `Repository`. Продакшн код передає імплементацію на основі бази даних. Тести передають мок або стаб. ## Мокування з gomock та mockgen Пакет [gomock](https://github.com/uber-go/mock) генерує мок-імплементації з визначень інтерфейсів. Команда `mockgen` читає вихідні файли і створює мок-структури, які записують виклики та повертають налаштовані значення. ```bash # Generate mocks for the Repository interface mockgen -source=service.go -destination=mocks/repository_mock.go -package=mocks ``` Використання згенерованого моку в тестах: ```go // service_test.go package order import ( "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "yourmodule/order/mocks" ) func TestService_Process_SavesValidOrder(t *testing.T) { ctrl := gomock.NewController(t) mockRepo := mocks.NewMockRepository(ctrl) order := &Order{ID: "123", Total: 99.99} // Expect Save to be called once with this order, return nil mockRepo.EXPECT(). Save(order). Return(nil). Times(1) svc := NewService(mockRepo) err := svc.Process(order) assert.NoError(t, err) } func TestService_Process_RejectsZeroTotal(t *testing.T) { ctrl := gomock.NewController(t) mockRepo := mocks.NewMockRepository(ctrl) // No calls expected to repo since validation fails first svc := NewService(mockRepo) err := svc.Process(&Order{ID: "456", Total: 0}) assert.ErrorIs(t, err, ErrInvalidTotal) } ``` Мок перевіряє, що `Save` була викликана рівно один раз з очікуваним аргументом. Якщо код, що тестується, ніколи не викликає `Save` або викликає її з іншими аргументами, тест провалюється. ## Використання testify для assertions та test suites [testify](https://github.com/stretchr/testify) надає функції assertions, які створюють чіткіші повідомлення про помилки, ніж ручні if-перевірки. Пакет `assert` продовжує виконання після помилок. Пакет `require` негайно зупиняє тест. ```go // user_test.go package user import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestParseUser(t *testing.T) { input := `{"id": "u1", "name": "Alice"}` user, err := ParseUser(input) require.NoError(t, err, "parsing should not fail") assert.Equal(t, "u1", user.ID) assert.Equal(t, "Alice", user.Name) assert.Empty(t, user.Email, "email should default to empty") } ``` Виклик `require.NoError` зупиняє тест, якщо парсинг не вдається, запобігаючи панікам nil pointer у наступних assertions. ## Тестування HTTP хендлерів з httptest Пакет `net/http/httptest` надає `ResponseRecorder` для захоплення виводу хендлера та `Server` для запуску локального тестового сервера. Більшість модульних тестів використовують `ResponseRecorder`, щоб уникнути мережевого навантаження. ```go // handler_test.go package api import ( "net/http" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" ) func TestHealthHandler(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/health", nil) rec := httptest.NewRecorder() HealthHandler(rec, req) assert.Equal(t, http.StatusOK, rec.Code) assert.JSONEq(t, `{"status": "ok"}`, rec.Body.String()) } ``` Для хендлерів, що викликають зовнішні сервіси, впроваджуйте мок-клієнт або використовуйте `httptest.NewServer` для створення фейкового бекенду. ## Поширені питання співбесід про тестування в Go Інтерв'юери оцінюють знання тестування через питання, які розкривають досвід роботи з реальними кодовими базами. Ось патерни, що відрізняють підготовлених кандидатів: **"Як би ви протестували функцію, що викликає зовнішній API?"** Визначте інтерфейс для HTTP клієнта, впровадьте його у функцію або структуру та надайте мок, що повертає заздалегідь визначені відповіді. Це усуває мережеві виклики і робить тести детермінованими. **"Коли б ви використовували стаб замість моку?"** Стаби повертають заготовлені відповіді без перевірки того, як їх викликали. Моки перевіряють, що певні методи були викликані з певними аргументами. Використовуйте стаби, коли повернене значення важливіше за взаємодію. Використовуйте моки, коли перевірка взаємодії є метою тесту. **"Що таке табличні тести і чому Go їх надає перевагу?"** Табличні тести визначають тестові випадки як дані в slice структур. Вони зменшують boilerplate, роблять додавання випадків тривіальним і створюють читабельний тестовий вивід з `t.Run`. Патерн відповідає перевазі Go явного, повторюваного коду над хитрими абстракціями. **"Як ви обробляєте тестові фікстури або setup, що спільний для кількох тестів?"** Використовуйте `TestMain` для setup та teardown на рівні пакету. Використовуйте допоміжні функції або тестові набори (з testify) для спільного setup між пов'язаними тестами. Уникайте глобального стану, що створює зв'язок між тестами. Готуєтесь до співбесіди з Go? [Модуль тестування Go](/technologies/go/interview-questions/testing) детально розглядає ці патерни з практичними питаннями. ## Запуск тестів з покриттям та виявленням гонок Команда `go test` приймає прапорці, що виявляють непротестовані шляхи коду та помилки конкурентності. ```bash # Run tests with coverage report go test -cover ./... # Generate HTML coverage visualization go test -coverprofile=coverage.out ./... go tool cover -html=coverage.out -o coverage.html # Detect race conditions (slower, but catches real bugs) go test -race ./... ``` Детектор гонок інструментує доступ до пам'яті та позначає одночасні читання та записи до однієї змінної. Запуск тестів з `-race` в CI виловлює помилки, які проявляються лише при певному таймінгу. ## Організація тестів у великих кодових базах Зі зростанням проектів організація тестів впливає на підтримуваність: - **Тести в тому ж пакеті** (`package foo`) мають доступ до неекспортованих функцій та полів. Використовуйте для модульних тестів, що перевіряють внутрішню поведінку. - **Тести у зовнішньому пакеті** (`package foo_test`) імпортують пакет як зовнішній код. Використовуйте для інтеграційних тестів та для перевірки публічного API. - **Каталоги testdata** зберігають фікстури, такі як JSON файли, golden outputs або seed бази даних. Go ігнорує їх під час збірки. - **Build tags** відділяють модульні тести від інтеграційних тестів, що потребують зовнішніх систем. Запустіть `go test -tags=integration ./...`, щоб їх включити. Більше про найкращі практики Go поза тестуванням можна знайти в [статті про патерни конкурентності](/blog/go/go-concurrency-goroutines-channels), яка охоплює горутини та канали. ## Що запам'ятати про тестування в Go - Пакет `testing` поставляється з Go і не потребує зовнішніх залежностей - Табличні тести зменшують дублювання і є очікуваним патерном на співбесідах - Інтерфейси забезпечують впровадження залежностей; визначайте їх у місці використання, а не в імплементації - `gomock` генерує моки з інтерфейсів; `testify` надає чистіші assertions - `httptest.ResponseRecorder` захоплює вивід хендлера без мережевого навантаження - Прапорець `-race` виявляє помилки конкурентності, які модульні тести інакше пропускають - Метрики покриття спрямовують увагу, але не гарантують коректність --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/uk/blog/go/go-testing-2026-unit-tests-mocks-interview