Shape

Quick start

Quick-start

Requirements

Terratest uses the Go testing framework. To use Terratest, you need to install:

  • Go (requires version >=1.13)

Setting up your project

The easiest way to get started with Terratest is to copy one of the examples and its corresponding tests from this repo. This quick start section uses a Terraform example, but check out the Examples section for other types of infrastructure code you can test (e.g., Packer, Kubernetes, etc).

  1. Create an examples and test folder.

  2. Copy all the files from the basic terraform example into the examples folder.

  3. Copy the basic terraform example test into the test folder.

  4. To configure dependencies, run:

     cd test
     go mod init "<MODULE_NAME>"
    

    Where <MODULE_NAME> is the name of your module, typically in the format github.com/<YOUR_USERNAME>/<YOUR_REPO_NAME>.

  5. To run the tests:

     cd test
     go test -v -timeout 30m
    

    (See Timeouts and logging for why the -timeout parameter is used.)

Terratest intro

The basic usage pattern for writing automated tests with Terratest is to:

  1. Write tests using Go’s built-in package testing: you create a file ending in _test.go and run tests with the go test command. E.g., go test my_test.go.
  2. Use Terratest to execute your real IaC tools (e.g., Terraform, Packer, etc.) to deploy real infrastructure (e.g., servers) in a real environment (e.g., AWS).
  3. Use the tools built into Terratest to validate that the infrastructure works correctly in that environment by making HTTP requests, API calls, SSH connections, etc.
  4. Undeploy everything at the end of the test.

To make this sort of testing easier, Terratest provides a variety of helper functions and patterns for common infrastructure testing tasks, such as testing Terraform code, testing Packer templates, testing Docker images, executing commands on servers over SSH, making HTTP requests, working with AWS APIs, and so on.

Example #1: Terraform “Hello, World”

Let’s start with the simplest possible Terraform code, which just outputs the text, “Hello, World” (if you’re new to Terraform, check out our Comprehensive Guide to Terraform):

Loading...

How can you test this code to be confident it works correctly? Well, let’s think about how you would test it manually:

  1. Run terraform init and terraform apply to execute the code.
  2. When apply finishes, check that the output variable says, “Hello, World”.
  3. When you’re done testing, run terraform destroy to clean everything up.

Using Terratest, you can write an automated test that performs the exact same steps! Here’s what the code looks like:

Loading...

This code does all the steps we mentioned above, including running terraform init, terraform apply, reading the output variable using terraform output, checking its value is what we expect, and running terraform destroy (using defer to run it at the end of the test, whether the test succeeds or fails). If you put this code in a file called terraform_hello_world_example_test.go, you can run it by executing go test, and you’ll see output that looks like this (truncated for readability):

$ go test -v
=== RUN   TestTerraformHelloWorldExample
Running command terraform with args [init]
Initializing provider plugins...
[...]
Terraform has been successfully initialized!
[...]
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
hello_world = "Hello, World!"
[...]
Running command terraform with args [destroy -force -input=false]
[...]
Destroy complete! Resources: 2 destroyed.
--- PASS: TestTerraformHelloWorldExample (149.36s)

Success!

Example #2: Terraform and AWS

Let’s now try out a more realistic Terraform example. Here is some Terraform code that deploys a simple web server in AWS:

Loading...

The code above deploys an EC2 Instance that is running an Ubuntu Amazon Machine Image (AMI). To keep this example simple, we specify a User Data script that, while the server is booting, fires up a dirt-simple web server that returns “Hello, World” on port 8080.

How can you test this code to be confident it works correctly? Well, let’s again think about how you would test it manually:

  1. Run terraform init and terraform apply to deploy the web server into your AWS account.
  2. When apply finishes, get the IP of the web server by reading the public_ip output variable.
  3. Open the IP in your web browser with port 8080 and make sure it says “Hello, World”. Note that it can take 1–2 minutes for the server to boot up, so you may have to retry a few times.
  4. When you’re done testing, run terraform destroy to clean everything up.

Here’s how we can automate the steps above using Terratest:

Loading...

This test code runs terraform init and terraform apply, reads the server IP using terraform output, makes HTTP requests to the web server (including plenty of retries to account for the server taking time to boot), checks the HTTP response is what we expect, and then runs terraform destroy at the end. If you put this code in a file called terraform_aws_hello_world_example_test.go, you can run just this test by passing the -run argument to go test as follows:

$ go test -v -run TestTerraformAwsHelloWorldExample -timeout 30m
=== RUN   TestTerraformAwsHelloWorldExample
Running command terraform with args [init]
Initializing provider plugins...
[...]
Terraform has been successfully initialized!
[...]
Running command terraform with args [apply -auto-approve]
aws_instance.example: Creating...
  associate_public_ip_address:       "" => "<computed>"
  availability_zone:                 "" => "<computed>"
  ephemeral_block_device.#:          "" => "<computed>"
  instance_type:                     "" => "t2.micro"
  key_name:                          "" => "<computed>"
[...]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Outputs:
public_ip = 52.67.41.31
[...]
Making an HTTP GET call to URL http://52.67.41.31:8080
dial tcp 52.67.41.31:8080: getsockopt: connection refused.
Sleeping for 5s and will try again.
Making an HTTP GET call to URL http://52.67.41.31:8080
dial tcp 52.67.41.31:8080: getsockopt: connection refused.
Sleeping for 5s and will try again.
Making an HTTP GET call to URL http://52.67.41.31:8080
Success!
[...]
Running command terraform with args [destroy -force -input=false]
[...]
Destroy complete! Resources: 2 destroyed.
--- PASS: TestTerraformAwsHelloWorldExample (149.36s)

Success! Now, every time you make a change to this Terraform code, the test code can run and make sure your web server works as expected.

Note that in the go test command above, we set -timeout 30m. This is because Go sets a default test time out of 10 minutes, and if your test take longer than that to run, Go will panic, and kill the test code part way through. This is not only annoying, but also prevents the clean up code from running (the terraform destroy), leaving you with lots of resources hanging in your AWS account. To prevent this, we always recommend setting a high test timeout; the test above doesn’t actually take anywhere near 30 minutes (typical runtime is ~3 minutes), but we give lots of extra buffer to be extra sure that the test always has a chance to finish cleanly.

Example #3: Docker

You can use Terratest for testing a variety of infrastructure code and not Terraform. For example, you can use it to test your Docker images:

Loading...

The Dockerfile above creates a simple Docker image that uses Ubuntu 18.04 as a base and writes the text “Hello, World!” to a text file. At this point, you should already know the drill. First, let’s think through how you’d test this Dockerfile manually:

  1. Run docker build to build the Docker image.
  2. Run the image via docker run.
  3. Check that the running Docker container has a text file with the text “Hello, World!” in it.

Here’s how you can use Terratest to automate this process:

Loading...

Instead of using Terraform helpers, this test code uses Terratest’s Docker helpers to run docker build, docker run, and check the contents of the text file. As before, you can run this test using go test!

Example #4: Kubernetes

Terratest also provides helpers for testing your Kubernetes code. For example, here’s a Kubernetes manifest you might want to test:

Loading...

This manifest deploys the Docker training webapp, a simple app that responds with the text “Hello, World!”, as a Kubernetes Deployment and exposes it to the outside world on port 5000 using a LoadBalancer.

To test this code manually, you would:

  1. Run kubectl apply to deploy the Docker training webapp.
  2. Use the Kubernetes APIs to figure out the endpoint to hit for the load balancer.
  3. Open the endpoint in your web browser on port 5000 and make sure it says “Hello, World”. Note that, depending on your Kubernetes cluster, it could take a minute or two for the Docker container to come p, so you may have to retry a few times.
  4. When you’re done testing, run kubectl delete to clean everything up.

Here’s how you automate this process with Terratest:

Loading...

The test code above uses Kuberenetes helpers built into Terratest to run kubectl apply, wait for the service to come up, get the service endpoint, make HTTP requests to the service (with plenty of retries), check the response is what we expect, and runs kubectly delete at the end. You run this test with go test as well!

Give it a shot!

The above is just a small taste of what you can do with Terratest. To learn more:

  1. Check out the examples and the corresponding automated tests for those examples for fully working (and tested!) sample code.
  2. Browse through the list of Terratest packages to get a sense of all the tools available in Terratest.
  3. Read our Testing Best Practices Guide.
  4. Check out real-world examples of Terratest usage in our open source infrastructure modules: Consul, Vault, Nomad.

Happy testing!

Built by Gruntwork

Your entire infrastructure. Defined as code. In about a day.

Explore Gruntwork.io