Terraform Module Testing Prompt
Test Terraform modules — terraform test (1.6+), terratest (Go), unit vs integration, mocking providers.
- Target user
- Engineers writing tests for Terraform modules
- Difficulty
- Advanced
- Tools
- Claude, ChatGPT
The prompt
You are a senior Terraform engineer who has built test suites for module libraries — integration tests with real cloud, unit-style tests, mocked providers. I will provide: - The module to test - Current testing (if any) - Goal Your job: 1. **Terraform test (1.6+)**: - Built-in test framework - `.tftest.hcl` files - Run as `terraform test` - Native, easier than Go 2. **For terratest (Go)**: - Apply real infrastructure - Assert via cloud SDK - Destroy - Slow but thorough 3. **For unit-style** (no apply): - Plan-only checks - validate - Quick feedback 4. **For mocking providers** (1.7+): - Mock provider data - No cloud needed - Fast unit tests 5. **For test patterns**: - Smoke test (does it apply?) - Output verification - Side effect checks - Failure paths 6. **For CI integration**: - Run on PR - Cleanup on failure - Test environment isolation 7. **For sandbox account**: - Dedicated for tests - Cleanup via tags - Cost controls 8. **For test data**: - Fixtures - Random suffix to avoid collisions - Per-test unique Mark DESTRUCTIVE: tests against production accounts (resource collisions), leftover test resources (cost), assertions hitting production data. --- Module: [DESCRIBE] Current testing: [DESCRIBE] Goal: [DESCRIBE]
Why this prompt works
Modules without tests rot silently. This prompt walks options.
How to use it
- Use
terraform testfor new modules. - Add terratest for thorough.
- Plan-only first, apply for integration.
- CI on PR.
Patterns
terraform test (native, 1.6+)
# tests/network.tftest.hcl
variables {
vpc_cidr = "10.99.0.0/16"
az_count = 2
}
# Test 1: plan only (fast)
run "plan_only" {
command = plan
assert {
condition = length(module.network.public_subnet_ids) == 2
error_message = "Expected 2 public subnets for az_count=2"
}
}
# Test 2: apply (real resources)
run "apply_and_verify" {
command = apply
assert {
condition = aws_vpc.main.cidr_block == "10.99.0.0/16"
error_message = "VPC CIDR doesn't match input"
}
assert {
condition = length(aws_subnet.private) == 2
error_message = "Expected 2 private subnets"
}
}
# Test 3: validation should fail with invalid input
run "validate_input" {
command = plan
variables {
vpc_cidr = "not-a-cidr"
}
expect_failures = [
var.vpc_cidr,
]
}
terraform test
terraform test -filter=tests/network.tftest.hcl
terraform test -verbose
Mocked provider (1.7+, native unit test)
# tests/with_mocks.tftest.hcl
mock_provider "aws" {
mock_resource "aws_vpc" {
defaults = {
id = "vpc-mock-12345"
cidr_block = "10.0.0.0/16"
}
}
}
run "test_with_mocks" {
command = plan
assert {
condition = module.network.vpc_id == "vpc-mock-12345"
error_message = "Mocked VPC ID not returned"
}
}
terratest (Go, integration)
// test/network_test.go
package test
import (
"testing"
"github.com/gruntwork-io/terratest/modules/aws"
"github.com/gruntwork-io/terratest/modules/terraform"
"github.com/stretchr/testify/assert"
)
func TestNetwork(t *testing.T) {
t.Parallel()
region := "us-east-1"
terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
TerraformDir: "../examples/basic",
Vars: map[string]interface{}{
"vpc_cidr": "10.99.0.0/16",
"az_count": 2,
},
EnvVars: map[string]string{
"AWS_DEFAULT_REGION": region,
},
})
defer terraform.Destroy(t, terraformOptions)
terraform.InitAndApply(t, terraformOptions)
vpcID := terraform.Output(t, terraformOptions, "vpc_id")
assert.NotEmpty(t, vpcID)
// Verify via AWS SDK
vpc := aws.GetVpcById(t, vpcID, region)
assert.Equal(t, "10.99.0.0/16", *vpc.CidrBlock)
subnetIDs := terraform.OutputList(t, terraformOptions, "private_subnet_ids")
assert.Len(t, subnetIDs, 2)
}
cd test
go mod init my_org/network_test
go get github.com/gruntwork-io/terratest/modules/terraform
go test -v -timeout 30m
CI integration (GitLab)
test-module:
image: hashicorp/terraform:1.9
variables:
AWS_DEFAULT_REGION: us-east-1
# Account: sandbox; cleanup tagged
before_script:
- export AWS_ACCESS_KEY_ID=$SANDBOX_AWS_ACCESS_KEY_ID
- export AWS_SECRET_ACCESS_KEY=$SANDBOX_AWS_SECRET_ACCESS_KEY
script:
- cd modules/network
- terraform test -verbose
artifacts:
when: always
paths: [test-results/]
after_script:
# Cleanup leaked resources
- ./scripts/cleanup-sandbox.sh
Test patterns
| Pattern | When |
|---|---|
| Plan-only | Fast feedback, validate logic |
| Apply + verify | Integration, real cloud |
| Mocked provider | Unit tests, no cloud |
| Negative tests | Validation rules |
| Idempotence | Apply twice; no changes second |
Common findings this catches
- Tests skip apply → false confidence.
- Test resources leaked → cleanup script.
- No negative tests → invalid input passes.
- Tests too slow for PR → split.
- Parallel test collisions → unique IDs.
- Mocked provider returns canned data → catches schema errors but not logic.
- CI auth in plaintext → masked variables.
When to escalate
- Cloud test infrastructure — eng / finance.
- Test framework migration — staged.
- Cross-team module test standards — coordinate.
Related prompts
-
Ansible Molecule Testing Prompt
Test Ansible roles with Molecule — scenarios, drivers (Docker/Podman/Vagrant), verifiers (Ansible/Testinfra), idempotence.
-
Terraform CI/CD: Atlantis, Cloud, GitOps Prompt
Choose and configure Terraform CI/CD — Atlantis, Terraform Cloud, Spacelift, custom CI; plan/apply workflows, approvals.
-
Terraform Module Composition Prompt
Design Terraform modules — input/output contracts, composition, versioning, public vs private registry, when to abstract.