Terraform Tests: Mock Data Sources with mock_provider and override_data
Mock data sources in Terraform tests using mock_provider and override_data, including terraform_remote_state. Pin data source values in .tftest.hcl run blocks with real HCL examples.
- #terraform
- #testing
- #mock_provider
- #troubleshooting
Fixing errors like this? Get 500 free DevOps AI prompts
500 copy-paste AI prompts for the stack you actually run — one PDF, free.
The Problem: Data Sources Need Real Lookups
Modules routinely read from data sources — data "aws_ami" "ubuntu", data "aws_caller_identity" "current", or a data "terraform_remote_state" "network" that pulls VPC and subnet IDs from another state file. When you run terraform test, these data sources normally execute a real read against the provider or backend. That means credentials, network access, and a data source that actually returns something.
For unit tests you want none of that. Terraform’s test framework (1.7+) lets you fake data sources with mock_provider and pin their returned values with override_data. Your test then runs offline and returns exactly the values your assertions expect.
Auto-Mocked Data Sources
The moment you add mock_provider "aws" {}, every aws data source in the module is mocked automatically. Terraform generates schema-valid fake values for their computed attributes — the read never touches AWS.
# ami.tftest.hcl
mock_provider "aws" {}
run "ami_id_is_used" {
command = plan
assert {
condition = aws_instance.web.ami == data.aws_ami.ubuntu.id
error_message = "Instance did not use the looked-up AMI id"
}
}
This passes because both sides reference the same mocked value. But the actual AMI id is random, so you cannot assert data.aws_ami.ubuntu.id == "ami-0abc123" — that value is invented each run. To control it, use override_data.
Pinning Values with override_data
override_data sets specific attributes on a specific data source. It has two parts: target (the data source address) and values (the map of attributes to force).
mock_provider "aws" {
override_data {
target = data.aws_ami.ubuntu
values = {
id = "ami-0abcd1234ef567890"
architecture = "x86_64"
}
}
}
run "instance_uses_pinned_ami" {
command = plan
assert {
condition = aws_instance.web.ami == "ami-0abcd1234ef567890"
error_message = "Instance AMI did not match the overridden data source id"
}
}
You can place override_data inside the mock_provider block (applies to every run in the file) or inside a single run block (applies only there). A run-level override takes precedence, which is how you test the same module against several data-source scenarios in one file.
run "gpu_variant" {
command = plan
override_data {
target = data.aws_ami.ubuntu
values = { id = "ami-gpu000111222333444" }
}
assert {
condition = aws_instance.web.ami == "ami-gpu000111222333444"
error_message = "GPU run did not receive the GPU AMI id"
}
}
Mocking terraform_remote_state
terraform_remote_state is a special case because it is not an aws (or any cloud) data source — it belongs to the built-in terraform provider. To mock it you still target it with override_data, but its output values live under an outputs attribute, which is itself a map. Override the whole outputs object:
# remote-state.tftest.hcl
mock_provider "aws" {}
run "subnets_come_from_remote_state" {
command = plan
override_data {
target = data.terraform_remote_state.network
values = {
outputs = {
vpc_id = "vpc-0aa11bb22cc33dd44"
subnet_ids = ["subnet-0aaa111", "subnet-0bbb222"]
}
}
}
assert {
condition = aws_instance.web.subnet_id == "subnet-0aaa111"
error_message = "Instance did not use the first subnet from remote state"
}
}
Because the real backend is never read, there is no need for the remote state to exist, and no backend credentials are required. Your module can consume data.terraform_remote_state.network.outputs.vpc_id exactly as it does in production, and the test supplies the values.
Step-by-Step Resolution
- Add
mock_provider "aws" {}to auto-mock all cloud data sources in the module. - For any data source whose value an assertion depends on, add an
override_datablock with the data sourcetarget. - Fill
valueswith only the attributes you care about; unmentioned computed attributes stay auto-generated. - For
terraform_remote_state, override theoutputsmap as a nested object rather than individual top-level keys. - Put shared overrides in the
mock_providerblock and scenario-specific ones in individualrunblocks (run-level wins). - Run
terraform testand confirm eachrunpasses; a failing assertion names the exact condition.
Gotchas
terraform_remote_stateoutputs are nested. Overrideoutputs = { ... }, notvpc_id = ...at the top level — the latter silently does nothing.- Overrides must match the schema. Setting an attribute that is not part of the data source’s schema causes an error; setting a list where a string is expected fails the plan.
- Auto-generated values are random. If an assertion reads an un-overridden computed attribute, it will be non-deterministic. Override anything you assert on.
- Mocking hides real read errors. A malformed filter that a real
aws_amilookup would reject still “succeeds” against the mock. Keep one integration test with a real provider for confidence.
Need scenario matrices generated fast? The DevOps AI prompt library at /prompts/?stack=terraform can scaffold multiple run blocks with distinct override_data values from a single description.
Frequently Asked Questions
How do I mock a data source in Terraform tests? Add a mock_provider for its provider (which auto-mocks all of that provider’s data sources), then use override_data with a target and values map to pin specific attributes.
How do I mock terraform_remote_state in a tftest.hcl file? Target data.terraform_remote_state.<name> with override_data and set the whole outputs map as a nested object; the real backend is never read.
What is the difference between override_data and override_resource? override_data pins attributes on a data source (a read), while override_resource pins attributes on a managed resource (a create). They share the same target/values syntax.
Can I use different data source values per run block? Yes. Put an override_data block inside a specific run block; run-level overrides take precedence over file-level ones, letting you test several scenarios in one file.
Do overrides need credentials or a live backend? No. Once the provider is mocked, no data source read hits the network, so no cloud credentials or remote backend access are required. For more patterns, see the Terraform guides.
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.