Testing Terraform

terraform test, Terratest, validate.

Advanced12 min · lesson 12 of 15

Infrastructure code deserves testing like any other code, and Terraform has a ladder of techniques from cheap-and-fast to thorough-and-slow. The cheap end runs on every change and catches most mistakes; the expensive end actually provisions real resources to prove they work. A good pipeline uses several rungs, heaviest sparingly.

The testing ladder
1fmt / validate
syntax + internal consistency
2plan review
human + policy on the diff
3terraform test
native, assert on plan/apply
4Terratest
real deploy, real assertions
Run the cheap rungs on every commit; run the real-deploy rungs on a schedule or before release.

The cheap rungs: fmt and validate

terraform fmt canonicalizes formatting (run it in CI as a check so diffs stay clean), and terraform validate checks that the configuration is internally consistent — valid HCL, correct argument types, references that resolve — without touching any cloud. These run in seconds, need no credentials, and belong on every commit as the first gate. They will not catch a logic error, but they catch the typos and type mismatches instantly.

terminal
$ terraform fmt -check -recursive # fail CI if anything is unformatted
$ terraform validate
Success! The configuration is valid.

The native test framework

terraform test (built in since 1.6) runs .tftest.hcl files that set variables, run a plan or apply, and assert on the results — including spinning up real resources in a run block and checking their attributes, then cleaning up automatically. It is the sweet spot for module authors: real assertions without a separate language. For the heaviest end, Terratest (a Go library) deploys infrastructure, hits it with real checks (make an HTTP request, query the API), and tears it down — true integration testing, run before releasing a module.

web.tftest.hcl
run "sets_correct_size" {
command = plan
variables { environment = "prod" }
assert {
condition = aws_instance.web.instance_type == "t3.large"
error_message = "prod must use t3.large"
}
}
Real-deploy tests cost money and need isolation
terraform test with apply, and Terratest, create actual cloud resources — they cost money and leave debris if a run crashes before cleanup. Run them in a dedicated throwaway account or sandbox project, never against prod, and ensure teardown runs even on failure (defer in Terratest, automatic in terraform test). Budget them for scheduled or pre-release runs, not every commit; keep the per-commit gate on fmt, validate, plan, and policy.