Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Terraform By James Joyner IV · · 9 min read

Terraform Tests: Mock Data Sources with mock_provider and override_data

Quick answer

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
Free toolkit

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

  1. Add mock_provider "aws" {} to auto-mock all cloud data sources in the module.
  2. For any data source whose value an assertion depends on, add an override_data block with the data source target.
  3. Fill values with only the attributes you care about; unmentioned computed attributes stay auto-generated.
  4. For terraform_remote_state, override the outputs map as a nested object rather than individual top-level keys.
  5. Put shared overrides in the mock_provider block and scenario-specific ones in individual run blocks (run-level wins).
  6. Run terraform test and confirm each run passes; a failing assertion names the exact condition.

Gotchas

  • terraform_remote_state outputs are nested. Override outputs = { ... }, not vpc_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_ami lookup 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.

Free download · 368-page PDF

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.