A Terraform module can look correct yet produce the wrong output or deploy something that does not respond as expected. Terratest tests the deployed result: it supplies Go helpers that run Terraform and let a test validate the resulting infrastructure. Its Go foundation makes tests flexible, but teams must write and maintain Go code. section 9.3.2 section 9.1.3
A basic Terratest test follows this sequence:
- Create a file ending in _test.go, declare its package, and import Go’s testing package and the Terratest Terraform package. Initialize dependencies in the tests directory with go mod init and go mod tidy. section 9.3.1 section 9.3.2
- Create terraform.Options to select the Terraform directory and pass input variables. Its TerraformBinary field can select terraform or tofu; the example reads this choice from an environment variable and defaults to terraform. section 9.3.2
- Schedule terraform.Destroy with defer, then call terraform.InitAndApply. Run module-specific checks afterward, such as comparing an output with the supplied input or checking an HTTP endpoint. Deferred cleanup still runs if the test fails. section 9.3.2
Test an input passed through a resource
A small module passes test_input into a terraform_data resource and exposes its output as test_output. A Terratest case can apply the module with a chosen input, read the output, and assert that the two match. This keeps the first test focused on observable module behavior rather than a complex deployment. section 9.3.2
Listing 9.10 Basic structure of a Terratest test
package tests
import ( #1
"os"
"testing" #2
"github.com/gruntwork-io/terratest/modules/terraform" #3
"github.com/stretchr/testify/assert"
)
func TestExample(t *testing.T) {
t.Parallel() #4
testInput := "test" #5
terraformBinary := os.Getenv("TERRATEST_BINARY") #6
if len(terraformBinary) <= 0 {
terraformBinary = "terraform" #7
}
terraformOptions := terraform.WithDefaultRetryableErrors(t,
&terraform.Options{
TerraformDir: "../",
TerraformBinary: terraformBinary, #8
Vars: map[string]interface{}{
"test_input": testInput, #9
},
})
defer terraform.Destroy(t, terraformOptions) #10
terraform.InitAndApply(t, terraformOptions) #11
testOutput := terraform.Output(t, terraformOptions, "test_output") #12
assert.Equal(t, testInput, testOutput) #13
}
#1 We import our packages.
#2 This is the same testing package as before.
#3 Now we also pull in Terratest.
#4 This allows multiple tests to run at the same time.
#5 This is what we’ll pass to the input and will expect from the output.
#6 We use this to switch between Terraform and OpenTofu.
#7 If the environment variable is not set, we default to Terraform.
#8 We use the binary we set above.
#9 We pass the test input to the Terraform module.
#10 We defer the destroy to ensure the resources are cleaned up after tests are run.
#11 We run the Terraform init and apply.
#12 We get the output from the Terraform module.
#13 Finally, we test that the output is equal to the input.
The test listing shows Go imports, a test function, and Terraform binary selection before the deployment checks. section 9.3.2
For broader checks, Terratest provides helper packages for cloud providers, HTTP, DNS, and other testing tasks; Go third-party libraries are also available. Its maturity and version flexibility make it distinct from the newer native Terraform testing framework, which writes tests in HCL but requires newer Terraform versions. Because infrastructure tests can run longer and leave orphaned resources, cleanup is part of the test design, not an afterthought. section 9.3.4 section 9.1.3 section 9.7
