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.
How terraform test
Works
Section titled “How terraform test Works”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.
Making a .tftest file
Section titled “Making a .tftest file”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
Section titled “The run Block”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.
variable "bucket_name" { type = string default = "test-bucket-1234"}
resource "aws_s3_bucket" "example" { bucket = var.bucket_name acl = "private"}
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
Section titled “The variables Block”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!
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
Section titled “The provider Block”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.
Putting It All Together
Section titled “Putting It All Together”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." }}
Conclusion
Section titled “Conclusion”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.