Автоматические тесты на Terratest: модульные тесты

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

Автоматические тесты на Terratest: модульные тесты

Сообщение 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 и карьера
Представь ситуацию. Ты написал модуль terraform, который поднимает виртуалку, балансировщик и пачку правил для группы безопасности. Локально прогнал terraform plan apply, все зелёное, все красиво. Спустя месяц коллега правит этот модуль, добавляет переменную, чуть меняет шаблон cloud-init - и катит в прод. А там балансировщик внезапно отвечает 502, потому что health-check стал смотреть не на тот порт. Узнали об этом, понятное дело, от пользователей.

Знакомо? С обычным кодом такие фокусы ловят юнит-тесты. Инфраструктура как код - это тоже код, и его точно так же можно и нужно тестировать. Вопрос только в том, что считать тестом. Проверить, что terraform validate не ругается, недостаточно: синтаксис может быть валидным, а инфраструктура - нерабочей. Настоящая проверка - это поднять ресурсы по-настоящему и убедиться, что они действительно делают то, что должны. Именно для этого и придумали Terratest.

Что такое Terratest и почему именно так

Terratest - это библиотека на языке Go от компании Gruntwork. Никакого своего DSL, никаких yaml-описаний тестов. Ты пишешь обычный Go-тест, который внутри себя дёргает terraform apply на реальном коде, ждёт, пока ресурсы поднимутся, делает к ним живые запросы и сверяет результат с ожиданием. После проверки тест сносит за собой всё, что создал.

Ключевая идея, которую важно сразу принять: Terratest проверяет инфраструктуру изнутри, на работающих ресурсах. Это не статический анализ. Это интеграционное тестирование по своей природе, даже когда мы называем его модульным. Ты не угадываешь, появится ли публичный IP у виртуалки - ты реально его получаешь из output и стучишься на него по ssh. Подход дорогой по времени (реальный apply в yandex cloud - это минуты, а не миллисекунды), зато даёт уверенность, которую не даст ничто другое.

Почему Go, а не python или bash? Во-первых, Terratest изначально на Go и тащит с собой готовые хелперы под terraform, http, ssh, ожидания с ретраями. Во-вторых, тесты компилируются, а значит опечатку в имени функции ты поймаешь до запуска, а не на третьей минуте apply. Бонусом: если ты пишешь на opentofu вместо terraform, Terratest умеет и с ним, достаточно указать нужный бинарь.

Изображение

Анатомия одного теста

Любой тест Terratest строится по одной и той же скелетной схеме, и её стоит выучить как таблицу умножения:
  • Описываем опции: где лежит код модуля и какие переменные ему передать.
  • Сразу ставим отложенный terraform destroy через defer - чтобы убраться за собой, даже если тест упал.
  • Запускаем InitAndApply - реальный init и apply.
  • Читаем output модуля.
  • Делаем ассерты: http-запрос к балансировщику, ssh на хост, сверка значения output.
Самое важное здесь - defer. Это конструкция Go, которая откладывает вызов до конца функции. Мы ставим terraform destroy сразу после описания опций, ещё до apply. Логика простая: что бы ни случилось дальше - паника, проваленный ассерт, таймаут - Go всё равно выполнит отложенный destroy. Без этого правила первый же упавший тест оставит в облаке висеть виртуалки, которые будут капать тебе на счёт. Запомни: defer destroy ставится первым, думается о нём в последнюю очередь.

Вот как выглядит минимальный осмысленный тест модуля. Тестируемый модуль поднимает виртуалку с nginx, отдаёт её публичный IP в output, а тест проверяет, что http действительно отвечает:

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

package test

import (
	"fmt"
	"testing"
	"time"

	"github.com/gruntwork-io/terratest/modules/http-helper"
	"github.com/gruntwork-io/terratest/modules/terraform"
	"github.com/stretchr/testify/assert"
)

func TestWebServerModule(t *testing.T) {
	terraformOptions := &terraform.Options{
		// путь к модулю, который тестируем
		TerraformDir: "../examples/web-server",
		Vars: map[string]interface{}{
			"vm_name":   "tt-web-test",
			"image_id":  "fd80mrhj8fl2oe87o4e1", // ubuntu из Yandex Cloud Marketplace
			"subnet_id": "e9bxxxxxxxxxxxxxxxxx",
		},
	}

	// убираемся за собой в любом случае
	defer terraform.Destroy(t, terraformOptions)

	// реальный init + apply
	terraform.InitAndApply(t, terraformOptions)

	// читаем output модуля
	publicIP := terraform.Output(t, terraformOptions, "external_ip")
	url := fmt.Sprintf("http://%s:80", publicIP)

	// http-запрос с ретраями: ждём, пока nginx прогреется
	expected := "Welcome to nginx"
	httphelper.HttpGetWithRetry(t, url, nil, 200, expected, 30, 10*time.Second)

	// заодно проверим, что output не пустой
	assert.NotEmpty(t, publicIP)
}
Обрати внимание на HttpGetWithRetry. Инфраструктура поднимается не мгновенно: виртуалка стартует, cloud-init ставит nginx, балансировщик прогревает health-check. Если бы мы дёрнули http один раз сразу после apply, мы бы почти гарантированно получили connection refused. Поэтому Terratest везде, где речь о готовности сервиса, предлагает функции с ретраями: 30 попыток с паузой 10 секунд - и только если за это время сервис не ожил, тест считается проваленным.

Внедрение зависимостей и тестовые этапы

Когда тестов становится много, всплывает боль: каждый из них делает свой полный apply и destroy, и прогон растягивается на десятки минут. Terratest предлагает два приёма, чтобы это лечить.

Первый - внедрение зависимостей. Не зашивай в тест конкретные id ресурсов и регион намертво. Передавай их через переменные окружения или через Vars, а уникальные имена генерируй на лету хелпером random.UniqueId(). Это позволяет одному и тому же тесту крутиться в разных окружениях и не конфликтовать с параллельными прогонами - имена вроде tt-web-a1b2c3 не пересекутся.

Второй приём - тестовые этапы, stages. Terratest умеет разбивать тест на именованные стадии (deploy, validate, teardown) и пропускать те, что ты явно отключил через переменные окружения. Смысл такой: пока отлаживаешь ассерты, ты один раз делаешь deploy, а потом гоняешь только validate сколько угодно раз, не пересоздавая инфраструктуру. Деньги и время экономятся колоссально.

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

func TestWithStages(t *testing.T) {
	opts := &terraform.Options{TerraformDir: "../examples/web-server"}

	// teardown пропустится, если выставить SKIP_teardown=true
	defer test_structure.RunTestStage(t, "teardown", func() {
		terraform.Destroy(t, opts)
	})

	test_structure.RunTestStage(t, "deploy", func() {
		terraform.InitAndApply(t, opts)
		test_structure.SaveTerraformOptions(t, opts.TerraformDir, opts)
	})

	test_structure.RunTestStage(t, "validate", func() {
		opts := test_structure.LoadTerraformOptions(t, "../examples/web-server")
		ip := terraform.Output(t, opts, "external_ip")
		assert.NotEmpty(t, ip)
	})
}
Что вообще считать модульным тестом инфраструктуры

Здесь терминология слегка путает. В Terratest "модульный тест" - это тест одного модуля terraform, развёрнутого изолированно, а не тест всей системы целиком. То есть ты берёшь самый маленький самодостаточный кусок (модуль web-server, модуль network, модуль database) и проверяешь именно его контракт: какие переменные на входе, какие output на выходе, какое поведение у поднятых ресурсов.

Хорошая практика - держать рядом с модулем папку examples с минимальным рабочим примером его использования, и тестировать именно этот пример. Так ты убиваешь двух зайцев: и тест получаешь, и живую документацию, как модуль подключать. А уже на следующем уровне поверх модульных тестов строят интеграционные, где несколько модулей собираются вместе.

Типичные грабли
  • Забыл defer destroy - и в облаке копятся зомби-ресурсы. Первое, что пишешь после Options, - отложенный Destroy.
  • Тесты в проде. Никогда не гоняй Terratest на боевом проекте и боевом каталоге. Только отдельный тестовый folder в yandex cloud, желательно с лимитами и алертами на расходы.
  • Нет ретраев. Проверяешь http или ssh сразу после apply - получаешь флаки-тесты, которые падают через раз. Всегда используй версии с ретраями.
  • Хардкод имён. Два параллельных прогона с одинаковым vm_name подерутся за имя. Спасает random.UniqueId().
  • Состояние от прошлого упавшего теста. Если destroy не доехал, остатки terraform state и ресурсы могут мешать. Заведи регулярную чистку тестового окружения.
Мини-лаба

Повторить руками стоит вот что. Возьми любой свой простой модуль на yandex cloud - пусть это будет одна виртуалка с nginx и публичным IP в output. Положи рядом папку examples с вызовом этого модуля. Установи Go и подтяни terratest через go mod. Напиши тест по скелету из урока: Options с путём к примеру, defer Destroy, InitAndApply, чтение output external_ip, HttpGetWithRetry на 80 порт. Запусти go test -v -timeout 30m. Посмотри, как тест реально поднимает виртуалку, ждёт nginx, проверяет ответ и сносит всё за собой. Потом специально сломай модуль (например, не открой 80 порт в security group) и убедись, что тест честно падает.

Контрольные вопросы
  • Почему destroy в тесте ставят через defer, а не просто в конце функции?
  • Зачем нужны http и ssh функции с ретраями вместо одиночного запроса?
  • Что в Terratest называют модульным тестом и чем он отличается от интеграционного?
  • Какую проблему решают тестовые этапы (stages) и почему они экономят время?
Итог: что запомнить

Terratest тестирует инфраструктуру как код по-взрослому: реально применяет terraform на тестовом окружении и проверяет живые ресурсы, а не строки в конфиге. Скелет любого теста - Options, defer destroy, InitAndApply, чтение output, ассерты с ретраями. Модульный тест - это проверка одного модуля через его пример. Внедрение зависимостей и stages превращают медленные тесты в управляемый процесс. Работает это и с terraform, и с opentofu. Один раз настроив такой контур, ты перестаёшь узнавать о сломанной инфраструктуре от пользователей.
👍4 ❤️3 🔥1 😄 🤔2
Аватара пользователя
Redtop43
Сообщения: 2
Зарегистрирован: 22 май 2026, 10:35

Re: Автоматические тесты на Terratest: модульные тесты

Сообщение Redtop43 »

Спасибо, наконец дошло зачем именно defer а не просто destroy в конце. Раньше у меня после падения теста реально оставались виртуалки висеть и я не понимал откуда счета.
👍1 ❤️ 🔥 😄 🤔
Аватара пользователя
kotlin3
Сообщения: 1
Зарегистрирован: 31 май 2026, 12:41

Re: Автоматические тесты на Terratest: модульные тесты

Сообщение kotlin3 »

А stages обязательно через переменные окружения гонять или можно как-то из IDE отдельный этап запустить? Хочу validate крутить без пересоздания, но пока не сообразил как SKIP правильно выставить.
👍1 ❤️1 🔥1 😄 🤔1
Ответить
← Предыдущая глава
Ручное тестирование Terraform и очистка ресурсов
Следующая глава →
Интеграционные и сквозные тесты, пирамида тестирования

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

Поделиться темой: ✈ Telegram VK
Похожие запросы: как вынести terraform в модуль и переиспользовать инфраструктурувходные и выходные переменные модуля terraform как передатьуправление lvm и cron через ansible тома и таймерыкак сделать бэкап docker volume и восстановить данныекак создать роль ansible с нуля и структура каталоговdocker volume и bind mount в чем разница и где хранятся данные

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

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

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