Reputation: 41
I am working on unit testing for terraform. For some modules, I have to authorized into AWS to be able to retrieve terraform data source. Is there anyway to mock or override data source for something like below?
data "aws_region" "current" {
}
Thank you in advance.
Upvotes: 4
Views: 5091
Reputation: 482
Terraform now supports provider mocking (since version 1.7.*). The documentation can be found here, but here is an example for posterity. Let's imagine you are testing a module that relies on a data s3_bucket:
# main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
}
}
}
module "credentials" {
source = "./modules/s3_data"
data_bucket_name = "my_company_bucket_name"
}
resource "local_file" "credentials_json" {
filename = "credentials.json"
content = jsonencode(module.credentials.data)
}
You would then implement your module call:
# ./modules/s3_data/main.tf
variable "data_bucket_name" {
type = string
}
data "aws_s3_object" "data_bucket" {
bucket = var.data_bucket_name
key = "credentials.json"
}
output "data" {
value = jsondecode(data.aws_s3_object.data_bucket.body)
}
You can use override_data
to override this call to the data s3 bucket:
# main.tftest.hcl
mock_provider "aws" {
override_data {
target = module.credentials.data.aws_s3_object.data_bucket
values = {
body = "{\"username\":\"username\",\"password\":\"password\"}"
}
}
}
run "test" {
assert {
condition = jsondecode(local_file.credentials_json.content).username == "username"
error_message = "incorrect username"
}
}
Upvotes: 0
Reputation: 320
Another super-labour-intensive solution would be to emulate aws api on localhost and redirect (normal) aws provider there. I've found https://github.com/localstack/localstack - https://docs.localstack.cloud/integrations/terraform/ may probably be helpful with this.
Upvotes: -1
Reputation: 74309
Terraform does not include any built-in means to mock the behavior of a provider. Module authors generally test their modules using integration testing rather than unit testing, e.g. by writing a testing-only Terraform configuration that calls the module in question with suitable arguments to exercise the behaviors the author wishes to test.
The testing process is then to run terraform apply
within that test configuration and observe it making the intended changes. Once you are finished testing you can run terraform destroy
to clean up the temporary infrastructure that the test configuration declared.
A typical Terraform module doesn't have much useful behavior in itself and instead is just a wrapper around provider behaviors, so integration testing is often a more practical approach than unit testing in order to achieve confidence that the module will behave as expected in real use.
If you particularly want to unit test a module, I think the best way to achieve your goal within the Terraform language itself is to think about working at the module level of abstraction rather than the resource level of abstraction. You can then use Module Composition techniques, like dependency inversion, so that you can pass your module fake input when you are testing it and real input when it's being used in a "real" configuration. The module itself would therefore no longer depend directly on the aws_region
data source.
However, it's unlikely that you'd be able to achieve unit testing in the purest sense with the Terraform language alone unless the module you are testing consists only of local computation (locals
and output
blocks, and local-compute-only resources) and doesn't interact with any remote systems at all. While you could certainly make a Terraform module that takes an AWS region as an argument, there's little the module could do with that value unless it is also interacting with the AWS provider.
A more extreme alternative would be to write your own aws
provider that contains the subset of resource type names you want to test with but whose implementations of them are all fake. You could then use your own fake aws
provider instead of the real one when you're running your tests, and thus avoid interacting with real AWS APIs at all.
This path is considerably more work of course, and so I would suggest to embark on it only if the value of unit testing your particular module(s) is high.
Upvotes: 4