Modern QA2026Terratest: Integration Testing with Real Infrastructure — tiles
Log inJoin
4 / 75 · 08 Infrastructure as Code Testing · Terraform Validation← prev⊞ allnext →☰ Read as one page

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

  1. Always use t.Parallel() -- Terratest tests are slow. Running them in parallel reduces total execution time dramatically.

  2. Always use defer terraform.Destroy() -- Place the destroy call immediately after creating options, before InitAndApply. This ensures cleanup happens even if the test fails.

  3. Use unique names -- random.UniqueId() prevents naming collisions when tests run in parallel or if a previous cleanup failed.

  4. 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/
  1. 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
}