Testify для Go: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Что такое Testify и зачем он нужен?

Testify — это одна из самых популярных библиотек для тестирования на языке Go. Она расширяет возможности стандартного пакета testing, предоставляя разработчикам мощные утилиты для написания чистых, читаемых и поддерживаемых тестов. Testify решает несколько ключевых проблем, с которыми сталкиваются Go-разработчики:

  • Упрощение утверждений (assertions): Вместо громоздких конструкций с if и t.Errorf, Testify предлагает лаконичные методы вроде assert.Equal.
  • Читаемость ошибок: При провале теста Testify выводит подробные сообщения, показывающие ожидаемые и фактические значения.
  • Создание моков (mocks): Встроенная поддержка моков позволяет легко изолировать тестируемый код от внешних зависимостей (базы данных, HTTP-клиенты, файловые системы).
  • Группировка тестов (suite): Позволяет объединять тесты в логические группы с общими настройками (Setup/Teardown).

Testify идеально подходит для проектов любого размера — от небольших утилит до крупных микросервисных архитектур. Она является де-факто стандартом для тестирования в Go-сообществе.

Установка

Testify устанавливается как обычная Go-зависимость. Убедитесь, что у вас установлен Go (версия 1.16 или выше) и инициализирован модуль (go mod init).

go get github.com/stretchr/testify

После выполнения команды Testify будет добавлен в ваш go.mod файл. Теперь вы можете импортировать его в тестовые файлы.

Быстрый старт

Давайте напишем простой тест для функции, которая складывает два числа. Создадим файл math.go:

package main

func Add(a, b int) int {
    return a + b
}

Теперь создадим тестовый файл math_test.go:

package main

import (
    "testing"
    "github.com/stretchr/testify/assert"
)

func TestAdd(t *testing.T) {
    result := Add(2, 3)
    expected := 5
    assert.Equal(t, expected, result, "2 + 3 должно быть равно 5")
}

Запустите тест:

go test -v

Вы увидите зеленый результат. Если изменить ожидаемое значение на 6, тест упадет с подробным сообщением об ошибке.

Основные методы и функции

1. assert.Equal

Описание: Проверяет, что два значения равны. Использует reflect.DeepEqual для сравнения сложных типов (слайсы, структуры, карты).

Параметры:

  • t — объект *testing.T
  • expected — ожидаемое значение
  • actual — фактическое значение
  • msg (опционально) — сообщение об ошибке

Пример:

func TestEqual(t *testing.T) {
    assert.Equal(t, 10, 5+5, "5+5 должно быть 10")
    assert.Equal(t, []int{1, 2, 3}, []int{1, 2, 3})
}

2. assert.NotEqual

Описание: Противоположность Equal. Проверяет, что два значения НЕ равны.

Параметры: Аналогичны Equal.

Пример:

func TestNotEqual(t *testing.T) {
    assert.NotEqual(t, 10, 5+4, "5+4 не равно 10")
}

3. assert.True / assert.False

Описание: Проверяет, что выражение истинно (True) или ложно (False).

Параметры:

  • t — объект *testing.T
  • condition — булево выражение
  • msg (опционально) — сообщение об ошибке

Пример:

func TestTrueFalse(t *testing.T) {
    assert.True(t, 5 > 3, "5 должно быть больше 3")
    assert.False(t, 5 < 3, "5 не должно быть меньше 3")
}

4. assert.Nil / assert.NotNil

Описание: Проверяет, что значение равно nil (Nil) или не равно nil (NotNil). Полезно для проверки ошибок.

Параметры: Аналогичны True.

Пример:

func TestNil(t *testing.T) {
    var err error = nil
    assert.Nil(t, err, "Ошибки быть не должно")

    err = errors.New("some error")
    assert.NotNil(t, err, "Ошибка должна быть")
}

5. assert.Contains / assert.NotContains

Описание: Проверяет, содержит ли строка, слайс или карта указанный элемент (Contains) или не содержит (NotContains).

Параметры:

  • t — объект *testing.T
  • container — контейнер (строка, слайс, карта)
  • element — искомый элемент
  • msg (опционально) — сообщение об ошибке

Пример:

func TestContains(t *testing.T) {
    assert.Contains(t, "Hello, World!", "World")
    assert.NotContains(t, "Hello, World!", "Go")
    assert.Contains(t, []int{1, 2, 3}, 2)
}

6. assert.Len

Описание: Проверяет, что длина строки, слайса, карты или массива равна ожидаемой.

Параметры:

  • t — объект *testing.T
  • object — объект, длину которого проверяем
  • length — ожидаемая длина
  • msg (опционально) — сообщение об ошибке

Пример:

func TestLen(t *testing.T) {
    assert.Len(t, "Go", 2)
    assert.Len(t, []int{1, 2, 3}, 3)
    assert.Len(t, map[string]int{"a": 1}, 1)
}

7. assert.Error / assert.NoError

Описание: Проверяет, что ошибка не равна nil (Error) или равна nil (NoError). Критически важно для тестирования функций, возвращающих ошибки.

Параметры:

  • t — объект *testing.T
  • err — ошибка
  • msg (опционально) — сообщение об ошибке

Пример:

func TestError(t *testing.T) {
    _, err := strconv.Atoi("abc")
    assert.Error(t, err, "Должна возникнуть ошибка")

    _, err = strconv.Atoi("123")
    assert.NoError(t, err, "Ошибки быть не должно")
}

8. assert.Panics / assert.NotPanics

Описание: Проверяет, что функция вызывает панику (Panics) или не вызывает (NotPanics). Полезно для тестирования защитного программирования.

Параметры:

  • t — объект *testing.T
  • fn — функция, которая может вызвать панику (обычно анонимная)
  • msg (опционально) — сообщение об ошибке

Пример:

func TestPanics(t *testing.T) {
    assert.Panics(t, func() {
        panic("something went wrong")
    }, "Функция должна вызвать панику")

    assert.NotPanics(t, func() {
        _ = 1 + 1
    }, "Функция не должна вызвать панику")
}

9. assert.InDelta

Описание: Проверяет, что два числа (float64) находятся в пределах заданной дельты друг от друга. Полезно для тестирования вычислений с плавающей точкой.

Параметры:

  • t — объект *testing.T
  • expected — ожидаемое значение
  • actual — фактическое значение
  • delta — максимально допустимая разница
  • msg (опционально) — сообщение об ошибке

Пример:

func TestInDelta(t *testing.T) {
    result := 10.0 / 3.0
    assert.InDelta(t, 3.333, result, 0.001, "Результат должен быть близок к 3.333")
}

10. assert.IsType

Описание: Проверяет, что фактическое значение имеет ожидаемый тип.

Параметры:

  • t — объект *testing.T
  • expectedType — значение ожидаемого типа (обычно nil указатель на тип)
  • actual — фактическое значение
  • msg (опционально) — сообщение об ошибке

Пример:

func TestIsType(t *testing.T) {
    var val interface{} = "hello"
    assert.IsType(t, "", val, "val должен быть строкой")

    val = 42
    assert.IsType(t, 0, val, "val должен быть int")
}

Реальный практический пример: тестирование HTTP-сервиса

Рассмотрим реальный сценарий: у нас есть HTTP-сервис, который сохраняет пользователей. Мы будем использовать моки для изоляции от базы данных.

Структура проекта:

  • user.go — модель пользователя
  • storage.go — интерфейс хранилища
  • handler.go — HTTP-обработчик
  • handler_test.go — тесты

user.go:

package main

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
    Age  int    `json:"age"`
}

storage.go:

package main

type Storage interface {
    SaveUser(user User) error
    GetUser(id int) (User, error)
}

handler.go:

package main

import (
    "encoding/json"
    "net/http"
    "strconv"
)

type UserHandler struct {
    storage Storage
}

func NewUserHandler(storage Storage) *UserHandler {
    return &UserHandler{storage: storage}
}

func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
    var user User
    if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
        http.Error(w, "invalid request body", http.StatusBadRequest)
        return
    }

    if user.Name == "" {
        http.Error(w, "name is required", http.StatusBadRequest)
        return                    

Рекомендаций