Skip to content

Testing

As you develop Terraform modules, ensuring that they work as expected before deploying them into production is crucial. Terraform provides the terraform test command to help you test your modules directly. By writing tests, you can verify that your infrastructure behaves correctly and consistently across different environments, avoiding potential issues before they arise.

A great blog on Terraform CI/CD and testing, written by Kevon Mayers and Welly Siauw can be read here.


The terraform test command allows you to run tests on your Terraform modules. These tests are written in dedicated files with the .tftest.hcl extension, and they live alongside your module’s Terraform configuration.

The test files contain assertions that check the behavior of your infrastructure, such as verifying that specific resources are created or that outputs meet expected values. When you run terraform test,

Terraform applies the module to a temporary environment, runs the tests, and destroys the infrastructure afterward to ensure that your environment remains clean.

Terraform executes run blocks in order, simulating a series of Terraform commands executing directly within the configuration directory. The order of the variables and provider blocks doesn’t matter.

Instead of creating a separate directory for tests, you are able to create .tftest.hcl files directly in the module’s root directory, giving the test scope to the resources you wish to validate!

As described over in the docs a Terraform test file is structured with the following elements:

  • One to many run blocks: These blocks define the logic for executing your module and checking its behavior.
  • Zero to one variables block: This block allows you to define test-specific input variables.
  • Zero to many provider blocks: You can configure providers specific to your test cases.

The run block is the heart of your test configuration. This block tells Terraform what to execute and what assertions to check. You can have one or multiple run blocks within a .tftest.hcl file, each testing a different scenario or configuration.

main.tf
variable "bucket_name" {
type = string
default = "test-bucket-1234"
}
resource "aws_s3_bucket" "example" {
bucket = var.bucket_name
acl = "private"
}
main.tftest.hcl
run "valid_s3_bucket" {
command = plan
description = "Test creation of an S3 bucket"
assert {
condition = aws_s3_bucket.example.bucket == "test-bucket-1234"
error_message = "The S3 bucket name does not match the expected value."
}
assert {
condition = aws_s3_bucket.example.acl == "private"
error_message = "The S3 bucket ACL is not set to private."
}
}

You are able to override variables and providers from your module, either globally or locally to each test and also

Read more on the run block configuration here

The variables block is used to define inputs for your test configuration. These inputs allow you to control the values passed into the resources or modules you are testing. The variables block is optional but very useful for dynamic testing.

variables {
bucket_name = "test-bucket-5678"
}

Declaring the variable globally in your .tftest will allow set the variables used in all the tests in your module.

You can also declare variables at the test level, allowing for different configurations to be run at test time!

main.tftest.hcl
run "valid_s3_bucket" {
variables {
bucket_name = "a-different-name"
}
assert {
condition = aws_s3_bucket.example.bucket == "test-bucket-1234"
error_message = "The S3 bucket name does not match the expected value."
}
}

You can learn more on how to use variables in your tests here.

The provider block allows you to configure the provider settings specifically for your tests. This block is optional and is used when your tests require specific provider configurations that differ from your main configuration.

provider "aws" {
region = "us-west-2"
}

Read more on how to use different providers in your tests here.

Here’s an example that uses all three blocks (run, variables, and provider) to test the creation of an AWS S3 bucket.

When you run terraform test, it will:

  • Initialize the environment and apply the module.
  • Run the assertions you’ve defined.
  • Clean up by destroying the resources after the test completes.
  • If the assertions pass, you’ll receive confirmation that the test succeeded.
  • If any assertions fail, Terraform will provide a clear error message so you can pinpoint what went wrong.
provider "aws" {
region = "us-west-2"
}
variables {
bucket_name = "test-bucket-1234"
}
run "basic_test" {
description = "Test creation of an S3 bucket with a specific name"
module "s3_bucket" {
source = "../"
bucket_name = var.bucket_name
}
assert {
condition = module.s3_bucket.bucket_name == var.bucket_name
error_message = "The S3 bucket name does not match the expected value."
}
assert {
condition = module.s3_bucket.acl == "private"
error_message = "The S3 bucket ACL is not set to private."
}
}

By using terraform test effectively, you can catch errors early, ensure infrastructure stability, and improve the overall quality and reliability of your Terraform code. Whether you’re testing individual resources or complex modules, this framework helps ensure that your infrastructure performs as expected in different environments and scenarios.

This testing capability empowers you to confidently make changes and deploy infrastructure knowing that it’s been thoroughly validated.