Интеграционные и сквозные тесты, пирамида тестирования

Рейтинг: 68.5% · 14 голосов
Подробный курс по Terraform и OpenTofu на примерах Yandex Cloud: ресурсы и провайдеры, состояние и бэкенды, модули, циклы и условия HCL, секреты и Lockbox, тестирование, CI/CD и командная работа. С актуализацией на 2026 (Terraform 1.3-1.10, OpenTofu).
Ответить
Аватара пользователя
Vlad_DevOps
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

Интеграционные и сквозные тесты, пирамида тестирования

Сообщение Vlad_DevOps »

Оглавление курса (47)
  1. Что такое инфраструктура как код и зачем она нужна
  2. Категории инструментов IaC: чем отличаются Ansible, Packer, Kubernetes и Terraform
  3. Чем Terraform отличается от Ansible, Pulumi и CloudFormation
  4. Как работает Terraform под капотом и кто такой OpenTofu
  5. Установка Terraform и OpenTofu, подготовка Yandex Cloud
  6. Первый ресурс: разворачиваем виртуальную машину в Yandex Cloud
  7. Веб-сервер на ВМ: метаданные, user-data и группы безопасности
  8. Переменные ввода и выходные значения в Terraform
  9. Кластер ВМ: группа экземпляров Yandex Compute Instance Group
  10. Балансировщик нагрузки: Application Load Balancer в Yandex Cloud
  11. Состояние Terraform: что такое tfstate и чем опасен локальный файл
  12. Удалённое хранилище состояния: бэкенд на Yandex Object Storage
  13. Изоляция состояния: рабочие области против раскладки по папкам
  14. Источники данных и terraform_remote_state: связываем компоненты
  15. Модули Terraform: переиспользуем инфраструктуру
  16. Входные, локальные и выходные переменные модуля
  17. Версионирование модулей и подводные камни
  18. Циклы в Terraform: параметр count
  19. Циклы в Terraform: for_each по множествам и картам
  20. Выражения for и строковые директивы
  21. Условная логика: count-трюк, for_each и директива if
  22. Встроенные функции, типы и выражения HCL
  23. Развёртывание без простоя: create_before_destroy
  24. Подводные камни Terraform и рефакторинг с блоком moved
  25. Управление секретами: основы и типы
  26. Инструменты управления секретами: Yandex Lockbox, Vault, SOPS
  27. Аутентификация провайдера и секреты: сервисные аккаунты и OIDC
  28. Секреты в ресурсах и состоянии, ephemeral-значения
  29. Провайдеры Terraform: установка, версии, required_providers
  30. Несколько копий одного провайдера: alias и мультизона
  31. Несколько провайдеров и аккаунтов: configuration_aliases
  32. Terraform, Docker и Kubernetes: Managed Kubernetes в Yandex Cloud
  33. Код Terraform промышленного уровня: почему это долго
  34. Мелкие компонуемые модули вместо монолита
  35. Тестируемые модули и проверки: validation, precondition, postcondition
  36. Версионирование, публикация модулей и Terragrunt
  37. Ручное тестирование Terraform и очистка ресурсов
  38. Автоматические тесты на Terratest: модульные тесты
  39. Интеграционные и сквозные тесты, пирамида тестирования (вы здесь)
  40. Нативный terraform test и статический анализ
  41. Внедрение Terraform в команде: процессы и культура
  42. Конвейер развёртывания прикладного кода
  43. Конвейер инфраструктурного кода и золотое правило Terraform
  44. Стиль, ревью и CI/CD-инструменты: Atlantis, TACOS, policy as code
  45. Что нового в Terraform 1.3-1.10: моды, импорт, провайдер-функции, Stacks
  46. OpenTofu в 2026: чем живёт форк и как мигрировать
  47. Сертификация HashiCorp Terraform Associate и карьера
Представь, что ты собрал инфраструктуру как конструктор: модуль сети, модуль базы, модуль с виртуалками, балансировщик сверху. Каждый кусок ты протестировал по отдельности, всё зелёное, ты доволен. Деплоишь в прод - и узнаёшь, что security group базы не пускает трафик с подсети приложения, потому что в модуле сети ты назвал её чуть иначе, чем ждёт модуль базы. Модульные тесты этого не поймали - они проверяли модули поодиночке. Поймать такое могут только тесты, которые гоняют несколько модулей вместе. Об этом и урок.

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

Зачем вообще уровни тестов, а не один большой

Логика простая: чем выше уровень теста, тем больше он покрывает, но тем он медленнее, дороже и капризнее. Если всё проверять только сквозными прогонами полной инфры - ты разоришься на облаке и будешь ждать каждый прогон по сорок минут. Если проверять только модули по отдельности - пропустишь ошибки на стыках. Поэтому тесты делят на слои.
  • Статический анализ - самый нижний и самый дешёвый слой. terraform validate, terraform fmt, плюс линтеры (tflint) и проверки безопасности (tfsec, checkov, trivy). Код даже не разворачивается - просто читается. Секунды на прогон, ноль рублей за облако.
  • Модульные (unit) тесты - проверяют один модуль в изоляции. Развернул сеть - проверил, что подсеть создалась с нужным CIDR, удалил. Минуты, копейки.
  • Интеграционные тесты - собирают несколько модулей вместе и проверяют, что они дружат на стыках. Сеть плюс база плюс приложение - и тест лезет по сети, проверяя, что connectivity есть.
  • Сквозные (e2e) тесты - разворачивают всю инфру так, как она лежит в проде, и дёргают её снаружи как настоящий пользователь. Самый честный, самый медленный, самый дорогой.
Получается пирамида: внизу широкое основание из дешёвых проверок, наверху - острый кончик из одного-двух сквозных сценариев. Если у тебя пирамида перевёрнута (куча e2e и почти ноль unit) - это классический антипаттерн "стакан мороженого", прогоны будут долгими и хрупкими.

Изображение

Интеграционные тесты: модули в связке

Интеграционный тест существует ровно потому, что отдельные модули могут быть корректными, а их соединение - нет. Типичный пример из жизни Yandex Cloud: модуль сети отдаёт id подсети, модуль ВМ его принимает, а security group забыл открыть порт между ними.

Тест на Terratest (это библиотека на Go, де-факто стандарт) обычно делает четыре вещи: terraform init и apply на корневой конфиг, который подключает несколько модулей, потом достаёт output-ы, потом проверяет реальное поведение (ping, http-запрос, подключение к БД), и в конце terraform destroy в defer, чтобы убрать за собой даже если тест упал.

Корневой конфиг, который тестируется, выглядит примерно так - тут специально две части, сеть и виртуалка, чтобы было что интегрировать:

Код: Выделить всё

terraform {
  required_providers {
    yandex = {
      source = "yandex-cloud/yandex"
    }
  }
}

provider "yandex" {
  zone = "ru-central1-a"
}

resource "yandex_vpc_network" "this" {
  name = "test-net-${var.suffix}"
}

resource "yandex_vpc_subnet" "this" {
  name           = "test-subnet-${var.suffix}"
  zone           = "ru-central1-a"
  network_id     = yandex_vpc_network.this.id
  v4_cidr_blocks = ["10.10.0.0/24"]
}

resource "yandex_compute_instance" "app" {
  name        = "test-app-${var.suffix}"
  platform_id = "standard-v3"
  zone        = "ru-central1-a"

  resources {
    cores  = 2
    memory = 2
  }

  boot_disk {
    initialize_params {
      image_id = var.image_id
    }
  }

  network_interface {
    subnet_id = yandex_vpc_subnet.this.id
    nat       = true
  }

  metadata = {
    user-data = file("${path.module}/cloud-init.yaml")
  }
}

output "external_ip" {
  value = yandex_compute_instance.app.network_interface.0.nat_ip_address
}
Интеграционный тест после apply возьмёт output external_ip и реально постучится по нему, проверяя, что внутри ВМ поднялось приложение и сеть с NAT настроены правильно. Если бы subnet_id не прокинулся между ресурсами - ВМ бы не получила сеть, и тест упал бы на стадии подключения, а не молча пропустил баг.

Параллельность и изоляция: главная боль

Тесты хочется гонять параллельно - иначе пайплайн тянется вечность. Но если два теста одновременно создадут yandex_vpc_network с именем "test-net", второй упадёт с конфликтом имён или, хуже, они переплетутся. Поэтому золотое правило: каждый прогон работает в своём изолированном пространстве с уникальными именами.

Как этого добиваются на практике:
  • Уникальный суффикс на каждое имя ресурса. В Terratest это random.UniqueId(), который подставляется в переменную suffix. В чистом HCL ту же роль играет random_id или random_pet. Никаких хардкод-имён в тестах.
  • Отдельный state на прогон. Своя папка, свой backend-ключ или своя temp-копия конфигурации - чтобы параллельные прогоны не дрались за один state-файл и не ловили блокировки.
  • Изоляция по namespace/каталогу. В Yandex Cloud это отдельный folder_id или даже отдельная папка под тесты, чтобы мусор не смешивался с боевыми ресурсами и легко вычищался.
  • Тег с меткой прогона, чтобы потом скриптом снести всё, что вдруг не удалилось.
Вот как генерится уникальное имя средствами terraform, если тесты пишешь без Go:

Код: Выделить всё

resource "random_id" "suffix" {
  byte_length = 4
}

resource "yandex_vpc_network" "this" {
  name        = "ci-net-${random_id.suffix.hex}"
  description = "ephemeral network for test run"

  labels = {
    purpose = "ci-test"
    run_id  = random_id.suffix.hex
  }
}
Теперь хоть десять прогонов одновременно - имена не столкнутся, а по метке run_id легко найти и прибить забытые ресурсы.

Этапы тестирования для скорости

Сквозной тест часто состоит из дорогих стадий: развернуть сеть, развернуть кластер, развернуть приложение, проверить, снести. Если ты отлаживаешь только проверку приложения, переразворачивать всю инфру на каждой итерации - пытка. Поэтому тест бьют на этапы (stages), и каждый можно пропускать.

Идея такая: первый прогон делает setup полностью, дальше при отладке ты пропускаешь setup и teardown через переменные окружения и гоняешь только нужный кусок против уже поднятой инфры. В Terratest это test_structure с RunTestStage и переменными вида SKIP_setup_network, SKIP_deploy_app, SKIP_teardown. Когда отладил - снимаешь все skip, и тест прогоняется целиком. Экономит часы.

Время и стоимость прогона - почему форма пирамиды именно такая

Грубая прикидка по слоям. Статика - секунды и бесплатно, гоняй на каждый коммит. Unit - минуты, копейки, гоняй на каждый пуш. Интеграционные - десятки минут, заметные деньги за облако, гоняй на pull request. E2e - десятки минут и больше, дорого, гоняй на merge в main или по ночам.

Вывод прямой: дешёвых проверок должно быть много, дорогих - мало. Не пытайся покрыть каждую мелочь сквозным тестом - покрой её статикой или unit-ом, а e2e оставь для критичных пользовательских сценариев "развернули всё - снаружи работает". Кстати, всё ровно так же справедливо для opentofu - это форк terraform, та же модель команд terraform plan apply, те же подходы к тестам.

Типичные грабли
  • Не вызвал destroy - и облако копит зомби-ресурсы, которые жрут бюджет. destroy всегда в defer/cleanup, выполняется даже при падении теста.
  • Хардкод имён - параллельные прогоны конфликтуют. Только уникальные суффиксы.
  • Перевёрнутая пирамида - всё проверяется e2e, пайплайн идёт час и флапает. Спускай проверки на нижние слои.
  • Флаки из-за общего state - два теста на одном backend-ключе ловят блокировки. Изолируй terraform state по прогонам.
  • Пропустил статику - и узнаёшь о невалидном HCL только после десяти минут apply. validate и линтеры - первым делом, до любого развёртывания.
  • Тестируешь руками то, что ловит fmt/validate - не трать дорогие слои на то, что бесплатно ловит нижний.
Мини-лаба
  • Возьми корневой конфиг из урока (сеть плюс ВМ) и добавь random_id-суффикс ко всем именам ресурсов.
  • Прогони цепочку руками: terraform fmt -check, потом terraform validate, потом terraform plan, и только потом apply. Прочувствуй, что первые две команды бесплатны и мгновенны.
  • После apply забери output external_ip и проверь связность снаружи (ping или curl) - это и есть ручной интеграционный тест.
  • Запусти apply во втором каталоге одновременно - убедись, что благодаря суффиксам имена не конфликтуют.
  • Обязательно сделай terraform destroy в обоих каталогах. Привыкай убирать за собой.
Контрольные вопросы
  • Чем интеграционный тест отличается от модульного и какой класс ошибок ловит именно он?
  • Почему в пирамиде тестирования e2e-тестов должно быть мало, а статики и unit - много?
  • Какими тремя приёмами добиваются изоляции параллельных прогонов и зачем нужен уникальный суффикс в именах?
  • Зачем тест разбивают на этапы и что дают переменные SKIP при отладке?
Итог: что запомнить
Дешёвых проверок - много, дорогих - мало. Это вся пирамида одной фразой.
Статический анализ - фундамент: validate, fmt, линтеры ловят ошибки за секунды и даром. Unit проверяют модули поодиночке, интеграционные - их связку и стыки, e2e - всю инфраструктуру как в проде снаружи. Параллелить тесты можно и нужно, но только с изоляцией: уникальные имена, отдельный state, свой folder/namespace. Дроби тесты на этапы, чтобы не переразворачивать всё ради одной проверки. И всегда чисти за собой через destroy - забытые ресурсы в Yandex Cloud стоят реальных денег. Терраформ-тесты - это не бюрократия, а способ спать спокойно после деплоя.
👍4 ❤️1 🔥1 😄 🤔2
Аватара пользователя
enjoyer_nikita
Сообщения: 1
Зарегистрирован: 22 май 2026, 06:55

Re: Интеграционные и сквозные тесты, пирамида тестирования

Сообщение enjoyer_nikita »

Спасибо, наконец дошло зачем разные уровни тестов а не один большой прогон. Вопрос: random_id пересоздается каждый apply или держится в state? боюсь что имена будут скакать.
👍2 ❤️1 🔥 😄 🤔1
Аватара пользователя
zfs_pro
Сообщения: 1
Зарегистрирован: 12 май 2026, 11:38

Re: Интеграционные и сквозные тесты, пирамида тестирования

Сообщение zfs_pro »

У меня как раз перевернутая пирамида была, все гонял через полный деплой и ждал по 40 минут. Теперь понял что половину можно отдать в validate и tflint.
👍2 ❤️ 🔥1 😄 🤔
Ответить
← Предыдущая глава
Автоматические тесты на Terratest: модульные тесты
Следующая глава →
Нативный terraform test и статический анализ

Все главы курса «Terraform: инфраструктура как код на Yandex Cloud»

Поделиться темой: ✈ Telegram VK
  • Похожие темы

Вернуться в «Terraform: инфраструктура как код на Yandex Cloud»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость