Когда мы только начинали работу над платформой, тестирование напоминало ручное управление парусником в шторм — много усилий, но результат непредсказуем. Разработчики вручную создавали тестовые среды, QA проверяли функционал через Postman, а о стабильности релизов можно было только мечтать. Каждый новый релиз был как лотерея: либо всё работает, либо начинается долгий процесс поиска причины багов в production.

Сегодня я расскажу, как мы построили систему автоматизированного тестирования на Go и Allure, k6 и JavaScript, которая трансформировала наш процесс тестирования из хаотичного в предсказуемый и эффективный. Эта система стала не просто инструментом проверки, а настоящим фундаментом для быстрого и безопасного развертывания новых фич.

Что мы получили в итоге:

  • Сократили время проверки новых фич с дней до часов. Теперь разработчики могут получить обратную связь буквально за час, а не ждать целый день или даже дольше.

  • Позволили безопасно деплоить изменения несколько раз в день. Это особенно важно в условиях agile-подхода, где скорость выпуска новых фич критична.

  • Освободили команду от рутинных задач, позволяя сосредоточиться на разработке новых фич. Вместо того чтобы тратить время на ручное тестирование, наши инженеры могут заниматься тем, что у них действительно получается лучше всего — писать код.

  • Создали атмосферу уверенности при выпуске нового кода. Теперь каждый релиз — это не стресс, а радость от видимых результатов работы.

Эволюция тестирования: от хаоса к системе

Было:

В начале пути всё выглядело довольно хаотично. Тестирование было полностью ручным, а процессы — крайне медленными. Представьте себе следующую картину: разработчик пишет код, затем DevOps-инженер вручную создает виртуальную машину, после чего QA-инженеры запускают тесты через Postman. Звучит как кошмар? Именно так всё и было.

// Псевдокод нашего прошлого
public void testServiceCreation() throws Exception {
    DevOps.createVmManually();          // 2 часа ожидания
    QA.runPostmanCollection();          // 50% false-positive
    if (Math.random() > 0.7) {          // 30% шанс, что всё упадёт
        markAsPassed();
    }
}

Да-да, когда-то мы писали на Java. И знаете что? Это было ещё то приключение.

Стало:

Сейчас всё кардинально изменилось. Мы перешли на Go, который идеально подходит для высокопроизводительных задач, и внедрили Allure для детального анализа тестов. Теперь тестирование стало не просто автоматическим, а умным. Вот как выглядит современный подход:

// Современный подход
func TestServiceCreation(t *testing.T) {
    svc := platform.NewService("payment-gateway")
    require.NoError(t, svc.Deploy())     // Автоматическое создание
    health := svc.CheckHealth()          // Валидация endpoints 
    assert.Equal(t, 200, health.Status)
    metrics := svc.GetPrometheusMetrics() // Проверка метрик
    assert.True(t, metrics.CPU < 0.7)
}

Ключевые изменения:

  1. Полная автоматизация: Теперь тесты выполняются без единого ручного шага. Это не только экономит время, но и исключает человеческий фактор.

  2. Скорость: Мы сократили время выполнения тестов с ~4 часов до 40 минут. Это колоссальный скачок, который позволяет нам двигаться быстрее.

  3. Надёжность: Благодаря Allure-отчётам мы можем видеть каждый шаг выполнения теста, анализировать ошибки и быстро находить их причины.

  4. Масштабируемость: Система легко адаптируется под новые сервисы и требования. Это особенно важно в условиях активного роста платформы.

Схема взаимодействия с платформой извне и ее внутренних компонент выглядит «примерно»  так. Конечно же, в реальности все гораздо сложнее:

Найдите несколько отличий с этой схемой:

Мы решили эту сложность декомпозировать на меньшие сложности, а именно выделить некие логически связанные большие блоки:

  • интеграция сервисов и брокеров;

  • обращения к API-плафтормы;

  • создание собственных сервисов пользователями платформы (прямая функциональность App.Farm);

  • обращения к UI-плафтормы.

Учитывая схему взаимодействия и стоящие пред нами задачи, система тестирования построена на трёх ключевых компонентах, которые работают как единый механизм. Давайте разберём каждый из них подробнее.

Ядро на Go

Go стал нашим выбором не случайно. Этот язык отлично подходит для задач, требующих высокой производительности и минимальной задержки. На данный момент у нас уже более 1000 тест-кейсов, которые покрывают все критические сценарии работы платформы.

Интеграция с Kubernetes API: Мы активно используем Kubernetes для управления контейнерами и автоматизации развертывания сервисов.

Поддержка различных протоколов: Наша система работает с gRPC, REST API, Kafka, Artemis и IBM MQ. Это позволяет нам тестировать практически любые взаимодействия между сервисами.

Allure Framework

Allure стал нашим главным инструментом для визуализации тестов. Он предоставляет:

  • Детальные отчёты с историей запусков.

  • Возможность просматривать трейсы запросов и шаги выполнения.

  • Интеграцию с CI/CD для удобного анализа результатов.

Инфраструктура

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

Статические тестовые данные: Все необходимые данные генерируются заранее, что значительно ускоряет процесс тестирования.

Параллельный запуск: Мы запускаем тесты параллельно, что еще больше сократило время выполнения всего flow.

E2E flow как сервис: Flow доступен другим командам, что делает его универсальным инструментом для тестирования.

Тестирование как сервис: Проект тестирования доступен другим сервисам, что позволяет ему быть  универсальным инструментом внутри подразделения для тестирования.

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

func TestKafkaIntegration(t *testing.T) {
    topic := kafka.NewTopic("transactions")
    defer topic.Cleanup()
    
    producer := topic.NewProducer()
    consumer := topic.NewConsumer()
    
    producer.Send("test-message")
    msg := consumer.Read()
    
    assert.Equal(t, "test-message", msg)
}

Проблемы и решения

Нестабильные тесты (Flaky Tests)

Около 20% тестов иногда падали из-за нестабильности тестового окружения. Это могло быть связано с колебаниями нагрузки, конфликтами при параллельном выполнении или временными сбоями внешних зависимостей.

Мы внедрили многоуровневый подход: ввели систему меток для маркировки нестабильных тестов, пропускаем flaky-тесты в основной сборке CI/CD.

func TestMain(m *testing.M) {
    if os.Getenv("SKIP_FLAKY") == "true" {
        allure.SkipTestsWithTag("flaky")
    }
    os.Exit(m.Run())
}

# Пропуск flaky-тестов
SKIP_FLAKY=true go test ./...

Добавили этап ночной верификации, где все помеченные тесты запускаются и анализируются. Это позволяет нам не терять покрытие важных сценариев, но при этом не замедлять основной процесс разработки.

Долгие проверки

Некоторые отдельные тесты выполнялись более 30 минут, что замедляло процесс CI/CD. Особенно это касалось тестов, требующих создания большого объема данных или взаимодействия с внешними системами.

Мы оптимизировали процесс несколькими способами:

  • Вынесли подготовку данных в претестовую фазу. Теперь статические данные генерируются заранее, что значительно ускоряет процесс тестирования.

  • Добавили механизм умных повторных попыток (smart retry).

func withRetry(attempts int, delay time.Duration, action func() error) error {
    var lastErr error
    for i := 0; i < attempts; i++ {
        err := action()
        if err == nil {
            return nil // Успех
        }
        lastErr = err
        time.Sleep(delay)
    }
    return lastErr
}
func TestWithRetry(t *testing.T) {
    allure.Test(t, allure.Description("Testing with smart retry mechanism"), func() {
        err := withRetry(5, 2*time.Second, func() error {
            return performUnstableOperation()
        })
        assert.NoError(t, err, "Operation should succeed after retries")
    })
}

Сложная отладка

При падении теста было сложно понять причину. Логи были огромными, а контекст ошибки часто терялся.

Для решения добавили детальные Allure-отчёты с логами запросов и полным описанием шагов, а также внедрили механизм автоматической записи ошибок и их контекста.

Работа с нагрузкой

Изначально нагрузочное тестирование проводилось разработчиками без должной экспертизы, что приводило к следующим проблемам:

  • Использование самописного генератора нагрузки;

  • Отсутствие профиля нагрузки;

  • Проблемы со сбором метрик (UDP style);

  • Недостаточное понимание важности нагрузочного тестирования;

  • Отсутствие выделенного стенда для тестирования.

После анализа различных инструментов был выбран k6, который:

  • Имеет подробную документацию;

  • Предоставляет гибкие настройки генерации нагрузки;

  • Поддерживает расширение функционала через модули на Golang;

  • Хранит профили нагрузки в виде кода;

  • Компилируется в готовые бинарники под нужную среду.

Однако в процессе работы были выявлены некоторые ограничения: отсутствие профилирования памяти при использовании собственных модулей и сложности при сборке с C++ кодом

Envtest — отличное дополнение к e2e

Несомненно, e2e тесты являются мощным инструментом. Такие тесты незаменимы для проверки реального поведения приложения, но все же они имеют свои недостатки в виде:

  • поздних обнаружений ошибок;

  • сложности настройки окружения;

  • нестабильных тестов, зависящих от инфрастуктуры;

  • медленной скорости выполнения;

  • долгих CI пайплайнов.

Стоит заметить, что envtest способен эффективно справляться с этими задачами. К примеру, они не зависят от внешней инфрастуктуры или кластера в отличие от тестов e2e, что помогло решить проблему с flaky-тестами. Для запуска тестов не нужен деплой в кластер, что решило проблему скорости подготовки окружения. Также можно легко протестировать логику любого оператора (Что такое оператор, рекомендую к прочтению статью https://habr.com/ru/companies/rshb/articles/845430/).

Тесты с использованием окружения не требуют полноценной инфраструктуры, что позволило эмулировать сценарии, которые в e2e-тестировании вопроизвести сложно. Еще из плюсов — легкое встраивание в процесс CI/CD проектов операторов. И то, что прямым образом влияет на скорость тестирование - это ошибки, которые отлавливаются до деплоя в кластер.

Таким образом envtest не только может решить проблемы e2e тестирования, но и отлично дополнить и улучшить процесс тестирования.
Вместе они формируют мощную, гибкую и надежную стратегию тестирования платформы отлично дополняя друг друга.

Результаты

После внедрения системы автоматизированного тестирования время тестовые прогоны сократилось в ~5 раз. Теперь мы можем проверять новые фичи за считанные часы, а не дни. Релизы теперь выходят в среднем 3-4 раза в неделю по каждому проекту компонента AppFarm — это позволяет нам быстрее реагировать на обратную связь и внедрять новые возможности. Команда стала увереннее в качестве кода и быстрее реагирует на изменения. Теперь каждый релиз — это не стресс, а радость от видимых результатов работы.

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


  1. arman1211
    12.05.2025 13:03

    Очень интересно стало. GO реально быстрее и проще , он и для API и для UI тестов подойдет? А то хочется автотесты писать, выбираю стек, пока выбирал , уже GO врывается