Terratest, hands-on

Lucas B.
4 min readMar 26, 2021

In this article I´m going to show how to manage and interact with a very basics working example of Terratest, the idea is to have a first Terraform + Terratest working prototype.

Let´s start with a very basic example of a working Terraform project, the structure and function is very simple, different declared variables that will output different predefined values.

main.tf

data "template_file" "example" {template = var.example}data "template_file" "example2" {template = var.example2}resource "local_file" "example" {content  = "${data.template_file.example.rendered} + ${data.template_file.example2.rendered}"filename = "example.txt"}resource "local_file" "example2" {content  = data.template_file.example2.renderedfilename = "example2.txt"}

outputs.tf

output "example" {value = data.template_file.example.rendered}output "example2" {value = data.template_file.example2.rendered}output "example_list" {value = var.example_list}output "example_map" {value = var.example_map}output "example_any" {value = var.example_any}

When this code is run locally it will show the following output

Outputs:example = "example"
example2 = "test"
example_list = tolist([])
example_map = tomap({})

Now we need to focus on Terratest, the project structure should include one folder with all the Terraform files, in my case “examples”, and one separated folder, in this case “test”, at the same level where to run the Terratest files

├── examples
│ ├── README.md
│ ├── example.txt
│ ├── example2.txt
│ ├── main.tf
│ ├── outputs.tf
│ ├── terraform.tfstate
│ ├── terraform.tfstate.backup
│ ├── terraform.tfvars
│ ├── variables.tf
│ └── versions.tf
└── test
├── Gopkg.lock
├── Gopkg.toml
├── go.mod
├── go.sum
├── terraform_basic_example_test.go
└── vendor

Make sure the go project has the correct setup, It should be placed in the right go project workplace, I´m my case I´ve created a go folder where all the packages and modules are placed and the go-workspace and inside src/testings I´ve placed all the Terraform and Terratest files needed

├── go
│ └── pkg
│ ├── mod
│ │ ├── cache
│ │ ├── cloud.google.com
│ │ ├── github.com
│ │ ├── golang.org
│ │ ├── google.golang.org
│ │ ├── gopkg.in
│ │ ├── k8s.io
│ │ └── sigs.k8s.io
│ └── sumdb
│ └── sum.golang.org
└── go-workspace
├── bin
├── pkg
│ ├── dep
│ │ └── sources
│ ├── mod
│ │ ├── cache
│ │ ├── github.com
│ │ ├── golang.org
│ │ └── gopkg.in
│ └── sumdb
│ └── sum.golang.org
└── src
└── testings
├── examples
└── test

Now when the setup is done let´s take a look to the go file, this file will generate all the infrastructure defined in Terraform and do the necessary testings, in this case we need to make sure the outputs have the right values.

package testimport ("testing""github.com/gruntwork-io/terratest/modules/terraform""github.com/stretchr/testify/assert")// An example of how to test the simple Terraform module in examples/terraform-basic-example using Terratest.func TestTerraformBasicExample(t *testing.T) {t.Parallel()expectedText := "test"expectedList := []string{expectedText}expectedMap := map[string]string{"expected": expectedText}terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{// website::tag::1::Set the path to the Terraform code that will be tested.// The path to where our Terraform code is locatedTerraformDir: "../examples",// Variables to pass to our Terraform code using -var optionsVars: map[string]interface{}{"example": expectedText,// We also can see how lists and maps translate between terratest and terraform."example_list": expectedList,"example_map":  expectedMap,},// Variables to pass to our Terraform code using -var-file optionsVarFiles: []string{"varfile.tfvars"},// Disable colors in Terraform commands so its easier to parse stdout/stderrNoColor: true,})// website::tag::4::Clean up resources with "terraform destroy". Using "defer" runs the command at the end of the test, whether the test succeeds or fails.// At the end of the test, run `terraform destroy` to clean up any resources that were createddefer terraform.Destroy(t, terraformOptions)// website::tag::2::Run "terraform init" and "terraform apply".// This will run `terraform init` and `terraform apply` and fail the test if there are any errorsterraform.InitAndApply(t, terraformOptions)// Run `terraform output` to get the values of output variablesactualTextExample := terraform.Output(t, terraformOptions, "example")actualTextExample2 := terraform.Output(t, terraformOptions, "example2")actualExampleList := terraform.OutputList(t, terraformOptions, "example_list")actualExampleMap := terraform.OutputMap(t, terraformOptions, "example_map")// website::tag::3::Check the output against expected values.// Verify we're getting back the outputs we expectassert.Equal(t, expectedText, actualTextExample)assert.Equal(t, expectedText, actualTextExample2)assert.Equal(t, expectedList, actualExampleList)assert.Equal(t, expectedMap, actualExampleMap)}

To execute Terratest just need to run this command

go test -v -run TestTerraformBasicExample

Will execute the Terraform project, generate de IaC, check based on the tests we´ve defined and finalize with a PASS or FAIL message

--- PASS: TestTerraformBasicExample (5.48s)
PASS
ok testings/test 5.684s

--

--