A lot of Terraform test suites we review are elaborate ways of asserting that Hashicorp's providers work. They spin up a real VPC, then check that the VPC has the CIDR you passed in. It passes, everyone feels good, and it caught nothing that a `plan` would not have. Testing infrastructure code is worth it, but only if you are clear about what could actually break.
Three layers, increasing cost
We think about Terraform testing as a small pyramid. The cheap layers run on every commit; the expensive layer runs rarely and deliberately.
- Static and plan checks - format, validate, policy, and assertions against the planned JSON. Milliseconds to seconds, run on every PR.
- Native `terraform test` - spins up resources or runs plan-only assertions against your module's logic. Minutes. Run on module changes.
- Terratest - real apply into a sandbox account, then real assertions (can I actually reach the endpoint, does the failover work). Tens of minutes and real money. Run nightly or pre-release.
Plan checks catch the most bugs per euro
The cheapest tests have the best return. Native `terraform test` can run in plan-only mode and assert on what the plan intends to do, with no resources created. We use this to lock down the conditional logic in modules - the `count` and `for_each` expressions that are easy to get subtly wrong - because that branching is where real module bugs live, not in whether AWS creates the subnet.
run "private_endpoints_create_when_enabled" {
command = plan
variables { enable_private_endpoints = true }
assert {
condition = length(aws_vpc_endpoint.this) == 3
error_message = "Expected 3 endpoints when private endpoints enabled"
}
}If a test only proves that AWS creates the subnet you asked for, you are testing Hashicorp, not your code.
Where Terratest still earns its keep
Terratest is worth the cost and the Go for one thing: behavior you cannot see in a plan. Does the load balancer actually return 200 after the apply? Does killing the primary trigger the failover you designed? Does the IAM policy you wrote actually let the role do the thing, and nothing else? Those are real questions a plan cannot answer. We keep our Terratest suite small and brutal - a handful of high-value end-to-end checks - because every test there costs real cloud spend and ten-plus minutes per run. A 90-test Terratest suite is usually a sign someone tried to use it as a unit-test framework.
One discipline that pays off: every Terratest run gets its own ephemeral namespace or sandbox account and tears itself down with `defer`. The day a test leaves orphaned resources running is the day finance asks why the test account costs more than staging.