# Что тестировать в Terraform, а что: не надо _Тестирование · TerraformLab Knowledge Base_ **TL;DR:** Инфраструктура, не приложение, тест-пирамиду применять буквально не стоит. Тестируй контракты модулей, бизнес-правила, сложные expressions, рефакторинги без destroy. Не тестируй что provider работает, что AWS-API отвечает 200, и тривиальный `name = var.name`. Цель, ловить регрессии, не доказывать корректность. ## Тест-пирамида для инфры не работает Классическая пирамида: 70% unit, 20% integration, 10% e2e. Для приложения ок. Для Terraform, проблематично. - **Unit-тест Terraform-модуля стоит дёшево** (`.tftest.hcl` + mock_provider), но проверяет узкое: «модуль выкатил то, что HCL сказал выкатить». От бага в провайдере или AWS-API он не защитит. - **Integration** дороже на порядок (поднять LocalStack/aws, минуты), но реально проверяет, что результат, рабочая инфра. - **E2E**, собственно production-deploy. Само развёртывание уже есть в pipeline; «отдельный e2e», обычно дублирование. Реалистичный профиль для Terraform-репо: 40% unit, 40% integration на LocalStack, 20% policy/compliance, e2e, production-pipeline. ## Что тестировать ### 1. Контракт модуля Модуль принимает `var.name` и выкатывает `aws_s3_bucket.this`. Тест: передал «foo» → в plan `aws_s3_bucket.this.bucket == "foo"`. Это страховка от случайного переименования переменной. ```hcl # tests/contract.tftest.hcl run "var_name_propagates" { command = plan variables { name = "foo" } assert { condition = aws_s3_bucket.this.bucket == "foo" error_message = "var.name does not reach bucket" } } ``` ### 2. Бизнес-правила (политика) Encryption обязателен. Tag CostCenter обязателен. Не публичный по умолчанию. Это уровень компании, не модуля. Лучше, policy-as-code ([tf-policy-as-code](/terraform/kb/tf-policy-as-code.md) / [terraform-compliance](/terraform/kb/terraform-compliance.md)) на plan-файл в CI. ### 3. Сложные expressions Простой `name = var.name` тестировать незачем. А вот: ```hcl locals { bucket_name = "${var.team}-${var.purpose}-${random_id.suffix.hex}" tags = merge( var.default_tags, { Team = var.team, ManagedBy = "terraform" }, ) } ``` Здесь логика. Тест: «при team=ai и purpose=logs имя начинается с ai-logs-». ### 4. Рефакторинги (moved-блоки) Перенесли `aws_s3_bucket.logs` в модуль. Тест на чистый plan: ```hcl run "no_diff_after_refactor" { command = plan # ассерт что plan ничего не делает } ``` Сам Terraform pluralized сказал «no changes», но без assert в тесте этот факт нигде не зафиксирован. ### 5. Преконды/посткондишены ```hcl variable "env" { type = string validation { condition = contains(["dev", "stage", "prod"], var.env) error_message = "env must be dev/stage/prod" } } ``` Тест с `expect_failures = [var.env]` при `env = "xyz"`, гарантирует, что validation сработает. См. [tf-test-framework](/terraform/kb/tf-test-framework.md). ## Что НЕ тестировать ### 1. Что HCL правильно описывает AWS API ```hcl resource "aws_s3_bucket" "this" { bucket = "foo" } ``` Тестировать «когда apply, в AWS появится бакет foo», бесполезно. Это работа HashiCorp + AWS SDK. Ты не тестируешь компилятор, ты тестируешь свой код. ### 2. Тривиальный pass-through ```hcl output "arn" { value = aws_s3_bucket.this.arn } ``` Тест «output arn равен ARN», бессмысленный. Нечего ломаться. ### 3. Что облако ведёт себя как облако «После apply бакет действительно отвечает 200 на HEAD», это smoke-test уровня операций, а не теста модуля. Делается в production через мониторинг, а не в test suite. ### 4. Performance Тестировать «apply 100 ресурсов за < 60 секунд», flaky всегда. Performance Terraform зависит от latency provider'а и сети. Если хочется, отдельный бенч раз в неделю, не в каждом PR. ## Уровни и инструменты | Уровень | Что | Чем | |---|---|---| | **Static analysis** | Синтаксис, форматирование, типичные ошибки | `terraform fmt -check`, `terraform validate`, [tf-checkov](/terraform/kb/tf-checkov.md) | | **Lint** | Стиль, depracated args, провайдерские best-practices | tflint с rule-set'ом | | **Unit (модуль в изоляции)** | Контракт модуля, наименования, бизнес-правила | `.tftest.hcl` + `mock_provider` | | **Integration** | Реально создаются ресурсы, cross-resource взаимодействия | `.tftest.hcl` с `command = apply` на LocalStack, либо Terratest | | **Policy** | Корпоративные правила (тэги, security) | OPA+Rego, terraform-compliance, Checkov | | **E2E** | Развёртывание prod-окружения | Сам production-pipeline | ## Golden plan Один лёгкий, но мощный тест: «текущий код даёт plan, побитово совпадающий с сохранённой эталонной строкой». При любом изменении (HCL, провайдер, модуль) diff. Ревьюер видит точно что изменилось. Реализация: ```bash terraform plan -out=plan.tfplan terraform show -no-color plan.tfplan > plan.golden ``` Закоммитили `plan.golden`. В CI: ```bash terraform plan -out=plan.tfplan terraform show -no-color plan.tfplan > plan.current diff plan.golden plan.current || exit 1 ``` Когда меняешь HCL, обновляешь golden. PR показывает diff в HCL и diff в golden, обе стороны видны. Полезно на root-модулях с zero-diff-ожиданием. ## Сколько тестов Не больше, чем оправдано. Признаки переборщил: - Тесты дольше, чем сам apply. - Чаще ломаются от обновления провайдера, чем от твоего кода. - В тестах больше copy-paste, чем в production-коде. - Никто не может объяснить, что именно этот тест ловит. Признаки недотестировал: - Boilerplate-ошибки доходят до prod. - Рефакторинги ломают что-то, что не было заметно в plan. - Бизнес-правила нарушаются (tag забыли, encryption выключили). Балансируй между. ## Подводные камни - **Тесты, обязательство, не актив.** Каждый тест надо поддерживать. Старый тест, который никто не понимает, но боится удалить, токсичный долг. - **Mock'и не ловят интеграционные баги.** Юнит-тест с `mock_provider` может pass'нуться, а реальный apply упадёт, например, потому что AWS API требует определённый порядок аргументов или формат имени. - **Дешёвый тест дорог в обслуживании.** Сценарий «при var.foo=true, plan показывает 5 ресурсов», простой, но при добавлении 6-го ломается. Лучше тестировать инварианты («каждый аккаунт имеет KMS-key»), чем точные числа. - **Тесты не заменяют код-ревью.** Хорошо написанный HCL ревьюится быстрее, чем плохой код с 100% test coverage. Тесты, *в дополнение*. - **Production-debug пишется тестами.** Каждый раз когда что-то сломалось в production, добавляй тест, который бы это поймал. Это единственный надёжный способ копить test suite, который реально ловит реальные баги. ## См. также - [Mock-провайдеры: mock_provider, override_resource, override_data](/terraform/kb/tf-test-mocks.md) - [Terratest: интеграционные тесты Terraform на Go](/terraform/kb/terratest-basics.md) - [terraform-compliance: BDD-проверки на plan-файл](/terraform/kb/terraform-compliance.md) - [OPA + Rego, policy as code для Terraform plan](/terraform/kb/tf-policy-as-code.md)