← Back to blog

2026-06-18 · 7 min read

Testing Terraform Modules with Terratest (and Cheaper Alternatives)

How to actually test your Terraform, from free static checks (validate, tflint, tfsec) to real integration tests with Terratest, so your modules don't break production.

#terraform#testing#iac#devops#go
Testing Terraform Modules with Terratest (and Cheaper Alternatives)

Testing Terraform Modules with Terratest (and Cheaper Alternatives)

"It applied, so it works" is how Terraform modules quietly rot. A module that worked last quarter breaks when a provider updates, a default changes, or someone refactors. Testing catches that. Here's the layered approach I use, cheap checks first, real tests where they earn their keep.

Layer 1: Static checks (free, fast, run on every PR)

Most regressions are caught here, in seconds, with no cloud cost:

terraform fmt -check -recursive   # formatting
terraform validate                # syntax + internal consistency
tflint                            # provider-specific lint, deprecated args
tfsec   # or: trivy config .      # security misconfig (open SGs, no encryption)

Wire these into CI and they gate every change. For a lot of modules, this layer is 80% of the value.

Layer 2: Native tests (terraform test)

Terraform 1.6+ ships a built-in test framework, HCL test files, no extra language:

# tests/defaults.tftest.hcl
run "uses_safe_defaults" {
  command = plan

  assert {
    condition     = aws_s3_bucket_public_access_block.this.block_public_acls == true
    error_message = "Bucket must block public ACLs by default"
  }
}

plan-based tests are fast and free (no real resources). Great for asserting your module's logic and defaults.

Layer 3: Terratest (real integration tests)

When you need to prove a module actually provisions working infrastructure, Terratest (Go) applies real resources, asserts against them, and tears them down:

func TestVpcModule(t *testing.T) {
  opts := &terraform.Options{TerraformDir: "../examples/basic"}
  defer terraform.Destroy(t, opts)        // always clean up
  terraform.InitAndApply(t, opts)

  vpcID := terraform.Output(t, opts, "vpc_id")
  assert.NotEmpty(t, vpcID)
  // e.g. use the AWS SDK to confirm the VPC really exists and is configured right
}

Terratest is powerful but it costs money and time (it spins up real resources), so reserve it for modules where "does it really work end to end" matters.

What to use when

ConcernToolCost
Formatting / syntaxfmt, validatefree
Lint / deprecationstflintfree
Security misconfigtfsec / checkovfree
Logic / defaultsterraform test (plan)free
Real provisioningTerratest / terraform test (apply)$ + time

Practical advice

  • Start at the top of the table. Most teams get huge value from just the free layers in CI and never need much Terratest.
  • Test examples, not the module directly. Keep examples/ dirs that consume your module; test those. They double as documentation.
  • Always defer destroy in Terratest, and run integration tests in a sandbox account with budget alerts.
  • Pin provider versions so a provider release doesn't silently change behaviour mid-test.

My terraform-aws-reference-architecture runs the free layers in CI on every push, the cheapest, highest-leverage testing you can add today.


Need your Terraform made reliable and testable? That's my consulting work, get in touch.

Share:LinkedInXWhatsApp

Related articles

Reactions & comments