1.4Terratest: Integration Testing with Real Infrastructure
Terratest (written in Go) deploys real infrastructure, runs assertions, and tears it down. This is the most thorough validation because it exercises the actual cloud APIs, but it is also the most expensive.
When to Use Terratest
Use Terratest for reusable modules that many teams depend on. Do not use it for one-off configurations -- the overhead is not justified.
package test
import (
"testing"
"github.com/gruntwork-io/terratest/modules/terraform"
"github.com/gruntwork-io/terratest/modules/aws"
"github.com/gruntwork-io/terratest/modules/random"
"github.com/stretchr/testify/assert"
)
func TestS3BucketIsEncrypted(t *testing.T) {
t.Parallel()
terraformOptions := &terraform.Options{
TerraformDir: "../modules/s3-data-bucket",
Vars: map[string]interface{}{
"bucket_name": "test-" + random.UniqueId(),
"environment": "test",
},
}
// Deploy real infrastructure
defer terraform.Destroy(t, terraformOptions)
terraform.InitAndApply(t, terraformOptions)
// Get the bucket name from Terraform output
bucketName := terraform.Output(t, terraformOptions, "bucket_name")
region := terraform.Output(t, terraformOptions, "region")
// Verify encryption is enabled on the actual AWS resource
encryption := aws.GetS3BucketEncryption(t, region, bucketName)
assert.Equal(t, "aws:kms", encryption)
// Verify versioning
versioning := aws.GetS3BucketVersioning(t, region, bucketName)
assert.Equal(t, "Enabled", versioning)
// Verify public access is blocked
publicAccess := aws.GetS3BucketPublicAccessBlock(t, region, bucketName)
assert.True(t, publicAccess.BlockPublicAcls)
assert.True(t, publicAccess.BlockPublicPolicy)
}
Terratest Best Practices
Always use
t.Parallel()-- Terratest tests are slow. Running them in parallel reduces total execution time dramatically.Always use
defer terraform.Destroy()-- Place the destroy call immediately after creating options, beforeInitAndApply. This ensures cleanup happens even if the test fails.Use unique names --
random.UniqueId()prevents naming collisions when tests run in parallel or if a previous cleanup failed.Set timeouts -- Cloud resource creation can be slow. Set explicit timeouts rather than relying on Go's default test timeout of 10 minutes:
go test -v -timeout 30m ./test/
- Use test stages for faster iteration -- Terratest supports skipping the deploy/destroy stages during development:
func TestVPC(t *testing.T) {
terraformOptions := &terraform.Options{
TerraformDir: "../modules/vpc",
}
// Skip deploy if SKIP_deploy is set
defer terraform.Destroy(t, terraformOptions)
terraform.InitAndApply(t, terraformOptions)
// Validation runs even when reusing existing infrastructure
vpcId := terraform.Output(t, terraformOptions, "vpc_id")
subnets := aws.GetSubnetsForVpc(t, vpcId, "us-east-1")
assert.Equal(t, 6, len(subnets)) // 3 public + 3 private
}