Terratest is a Go library that lets you write automated integration tests for your infrastructure code, primarily Terraform.

Here’s a simple Terratest example. Imagine you have a Terraform module that provisions an S3 bucket.

package main

import (
	"testing"

	"github.com/gruntwork-io/terratest/modules/terraform"
)

func TestTerraformAwsS3Bucket(t *testing.T) {
	// Configure Terraform
	terraformOptions := terraform.WithDefaultCliVersion(t, nil, "./modules/s3-bucket")

	// Apply the Terraform code
	terraform.InitAndApply(t, terraformOptions)

	// Defer the destroy, so it runs even if tests fail
	defer terraform.Destroy(t, terraformOptions)

	// Assertions
	// You can call Terraform output, check AWS API, etc.
	// For example, here we'd check if the bucket exists.
	// bucketName := terraform.Output(t, terraformOptions, "bucket_name")
	// assert.Equal(t, "my-expected-bucket-name", bucketName)
}

This test does three things:

  1. terraform.WithDefaultCliVersion: Sets up the Terraform CLI environment for your test. It points to your Terraform module directory (./modules/s3-bucket).
  2. terraform.InitAndApply: Runs terraform init and terraform apply on the specified module. This provisions your infrastructure.
  3. defer terraform.Destroy: Schedules terraform destroy to run after the test function finishes, cleaning up the provisioned resources.

The real power comes from the assertions you write after InitAndApply. You can inspect the created infrastructure. For instance, you could use AWS SDKs within your Go test to verify S3 bucket policies, check EC2 instance states, or confirm database configurations. Terratest provides helper functions for common cloud providers and tools, making these checks easier.

The core problem Terratest solves is the gap between unit tests for your application code and manually verifying your infrastructure. Manually clicking around the cloud console or running terraform plan and terraform apply repeatedly is slow, error-prone, and doesn’t scale. Terratest automates this, giving you confidence that your infrastructure code behaves as expected in a real environment.

Internally, Terratest works by executing Terraform commands in a temporary directory. It captures stdout and stderr, allowing you to parse outputs and errors. It also provides functions to interact with cloud provider APIs, SSH into provisioned instances, and run commands remotely.

You control the tests by writing Go code. This means you can use all the power of Go: loops, conditionals, functions, and external libraries. You define the desired state of your infrastructure in your Terraform code and then write Go assertions in Terratest to verify that the actual state matches your expectations.

One thing most people don’t know is how easily you can manage multiple Terraform states within a single Terratest. You can create different terraform.Options structs, each pointing to a different Terraform module or directory, and then InitAndApply them independently or in sequence within the same Test... function. This allows you to test complex interdependencies, like provisioning a VPC, then an EC2 instance within that VPC, and then an RDS database that the EC2 instance needs to connect to, all orchestrated by a single Go test.

The next concept to explore is using Terratest to test custom Terraform providers.

Want structured learning?

Take the full Terraform course →