Расскажите о своем опыте написания тестов в Go. Какие инструменты и подходы вы используете?

Ответ

Да, я имею обширный опыт в написании тестов на Go. В основном я использую стандартный пакет testing и придерживаюсь подхода табличных тестов (table-driven tests), так как он позволяет покрыть множество кейсов компактным и легко расширяемым кодом.

Пример табличного теста:

func TestAdd(t *testing.T) {
    testCases := []struct {
        name     string
        a, b     int
        expected int
    }{
        {"Положительные числа", 2, 3, 5},
        {"Отрицательные числа", -1, -1, -2},
        {"Смешанные числа", -5, 10, 5},
        {"Нулевые значения", 0, 0, 0},
    }

    for _, tc := range testCases {
        t.Run(tc.name, func(t *testing.T) {
            if result := Add(tc.a, tc.b); result != tc.expected {
                t.Errorf("Add(%d, %d) = %d; ожидалось %d", tc.a, tc.b, result, tc.expected)
            }
        })
    }
}

Помимо стандартной библиотеки, я активно использую:

  • testify/assert и testify/require: для более читаемых и выразительных проверок. assert продолжает выполнение теста после ошибки, а require немедленно его останавливает.
  • gomock или testify/mock: для создания мок-объектов (mock objects) и тестирования компонентов в изоляции от их зависимостей (например, базы данных или внешних API).

Также я знаком с написанием интеграционных тестов, где проверяется взаимодействие нескольких компонентов системы.

Ответ 18+ 🔞

А, опыт в го-тестах? Да у меня его, блядь, овердохуища, как говна за баней! Сижу, значит, на стандартном testing, как дед на печи, но подход у меня, сука, выверенный — табличные тесты, ёпта! Потому что это ж, блядь, как универсальная отвёртка: и компактно, и под любую жопу кейса подлезешь, и расширить — раз плюнуть.

Смотри, как я обычно это делаю, в рот меня чих-пых:

func TestAdd(t *testing.T) {
    testCases := []struct {
        name     string
        a, b     int
        expected int
    }{
        {"Положительные числа", 2, 3, 5},
        {"Отрицательные числа", -1, -1, -2},
        {"Смешанные числа", -5, 10, 5},
        {"Нулевые значения", 0, 0, 0},
    }

    for _, tc := range testCases {
        t.Run(tc.name, func(t *testing.T) {
            if result := Add(tc.a, tc.b); result != tc.expected {
                t.Errorf("Add(%d, %d) = %d; ожидалось %d", tc.a, tc.b, result, tc.expected)
            }
        })
    }
}

Ну а что, одной стандартной библиотекой сыт не будешь, это ж как хлеб без соли. Поэтому тащу в проект, блядь, testify/assert с testify/require — чтобы проверки читались, как похабный анекдот, а не как инструкция к стиралке. assert — это типа «ну ошибся и ошибся, идём дальше», а require — это уже «ёбта, всё, пиздец, дальше нет смысла, падай!».

А когда нужно от всего этого, блядь, внешнего мира отгородиться — базы там, апишки всякие — тут в ход идут моки. gomock или testify/mock, без разницы, главное — чтобы заглушки были качественные, а не как из говна и палок. Создал фантомную зависимость, подсунул её в код и смотришь, не обосрётся ли он на ровном месте.

И да, интеграционные тесты — это отдельная песня, блядь. Когда нужно проверить, как эта, блядь, куча железок друг друга понимает. Тут уже не до шуток, тут волнение ебать, чтобы всё как по маслу пошло, а не накрылось медным тазом на первом же ходу. В общем, опыт есть, и не маленький, хуй с горы!