← Назад к вопросам

Как много пишешь Unit-тесты?

1.3 Junior🔥 182 комментариев
#Тестирование

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Мой подход к юнит-тестированию в Go

Я пишу юнит-тесты систематически и считаю их неотъемлемой частью процесса разработки. В Go это особенно важно, поскольку язык имеет встроенную поддержку тестирования через пакет testing и развитую экосистему инструментов.

Объём тестового покрытия

В среднем, 70-90% кода покрывается юнит-тестами, в зависимости от типа проекта:

  • Для библиотек и утилит — стремимся к 90%+ покрытию, так как это публичный API
  • Для бизнес-логики ядра приложения — 80-90%
  • Для интеграционных адаптеров — 60-70% (дополняются интеграционными тестами)
  • Для утилитарных/вспомогательных функций — 100%

Практические принципы написания тестов

Я следую нескольким ключевым принципам:

1. Тестируем поведение, а не реализацию

// ПЛОХО: тест зависит от внутренней структуры
func TestUserService_AddUser(t *testing.T) {
    s := &UserService{db: mockDB}
    s.AddUser("test")
    if s.counter != 1 { // Тест знает о counter
        t.Error("counter not incremented")
    }
}

// ХОРОШО: тест проверяет наблюдаемое поведение
func TestUserService_AddUser(t *testing.T) {
    mockDB := new(MockDB)
    mockDB.On("Save", mock.Anything).Return(nil)
    
    s := NewUserService(mockDB)
    err := s.AddUser("test")
    
    assert.NoError(t, err)
    mockDB.AssertCalled(t, "Save", "test")
}

2. Используем table-driven tests для комплексного покрытия

func TestCalculateDiscount(t *testing.T) {
    tests := []struct {
        name        string
        userType    string
        amount      float64
        expected    float64
        shouldError bool
    }{
        {"regular user small amount", "regular", 100, 0, false},
        {"vip user large amount", "vip", 1000, 100, false},
        {"invalid user type", "unknown", 100, 0, true},
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            result, err := CalculateDiscount(tt.userType, tt.amount)
            
            if tt.shouldError {
                assert.Error(t, err)
            } else {
                assert.NoError(t, err)
                assert.Equal(t, tt.expected, result)
            }
        })
    }
}

3. Мокируем зависимости через интерфейсы

type Storage interface {
    Save(data []byte) error
    Load(id string) ([]byte, error)
}

type Processor struct {
    storage Storage
}

func TestProcessor_HandleData(t *testing.T) {
    mockStorage := new(MockStorage)
    mockStorage.On("Save", []byte("processed")).Return(nil)
    
    processor := &Processor{storage: mockStorage}
    err := processor.HandleData("input")
    
    assert.NoError(t, err)
    mockStorage.AssertExpectations(t)
}

Инструментарий и практики

Основные инструменты:

  • Стандартный testing — основа всех тестов
  • testify — для утверждений (assert) и моков (mock)
  • gomock — для генерации моков по интерфейсам
  • goconvey — для BDD-стиля тестирования
  • golang/mock — официальная библиотека для мокинга

CI/CD интеграция:

  • Автоматический запуск тестов на каждый коммит
  • Проверка покрытия через go test -cover
  • Использование sonar-go для анализа качества
  • Бенчмаркинг критичных участков кода

Когда я НЕ пишу юнит-тесты

Есть исключительные случаи:

  1. Прототипы и PoC — когда код предназначен только для демонстрации концепции
  2. Визуальные/UI компоненты — здесь более уместны интеграционные тесты
  3. Тривиальные геттеры/сеттеры — если они не содержат логики
  4. Генераторы кода — тестируется результат генерации, а не сам генератор

Экономическая эффективность

Юнит-тесты в Go особенно эффективны благодаря:

  • Быстрому выполнению — тесты запускаются за миллисекунды
  • Параллельному запускуt.Parallel() для независимых тестов
  • Минимальным зависимостям — нет необходимости в тяжелых фреймворках

Мой опыт показывает, что время, инвестированное в юнит-тестирование, окупается:

  • Снижение количества багов в production на 40-60%
  • Ускорение рефакторинга и модификации кода
  • Улучшение дизайна кода через необходимость создания тестируемых компонентов
  • Эффективная документация через примеры использования

В Go культура тестирования — это часть инженерной дисциплины, и я полностью её разделяю, находя баланс между качеством тестов и скоростью разработки.

Как много пишешь Unit-тесты? | PrepBro