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

mock_provider vs override_resource vs override_data in Terraform Tests

Quick answer

Decide between mock_provider, override_resource, and override_data in .tftest.hcl. When to mock a whole provider vs override a single resource or data source in Terraform 1.7+ tests.

  • #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 Decision You Are Facing

Terraform 1.7+ ships three overlapping ways to fake infrastructure in a .tftest.hcl file: mock_provider, override_resource, and override_data. They solve related problems, and it is easy to reach for the wrong one, which leads to either brittle tests or tests that still hit real APIs.

Here is the short version:

  • mock_provider — fake an entire provider. Broad. Use it as your default for offline unit tests.
  • override_resource — pin the computed values of one specific resource. Narrow. Use it for a resource whose real values you need to assert against.
  • override_data — pin the computed values of one specific data source. Narrow. Use it for a single data read (like a lookup or terraform_remote_state).

mock_provider: Fake the Whole Provider

mock_provider replaces every resource and data source for a provider with generated fake values. Computed attributes get placeholder values automatically, so a plan or apply runs completely offline with no credentials.

mock_provider "aws" {}

run "bucket_gets_expected_name" {
  command = plan

  variables {
    environment = "staging"
  }

  assert {
    condition     = aws_s3_bucket.logs.bucket == "acme-logs-staging"
    error_message = "bucket name did not follow the naming convention"
  }
}

Every AWS resource and data source is now mocked. You did not have to enumerate anything. This is the right default when you are testing your own logic (naming, count, conditionals, tags) rather than a specific value returned by the cloud.

You can also seed defaults per resource type inside the block:

mock_provider "aws" {
  mock_resource "aws_s3_bucket" {
    defaults = {
      arn = "arn:aws:s3:::mocked-bucket"
    }
  }
}

Reach for it when: you want a fully offline unit test and you do not care about the exact values of most computed attributes.

override_resource: Pin One Resource

Sometimes a mock provider gives you a placeholder value, but a downstream expression depends on a specific computed attribute of one resource. override_resource lets you fix that resource’s values precisely, whether or not the provider is mocked.

mock_provider "aws" {}

run "cert_arn_flows_to_listener" {
  command = plan

  override_resource {
    target = aws_acm_certificate.this
    values = {
      arn = "arn:aws:acm:us-east-1:111111111111:certificate/abc"
    }
  }

  assert {
    condition     = aws_lb_listener.https.certificate_arn == "arn:aws:acm:us-east-1:111111111111:certificate/abc"
    error_message = "listener did not pick up the certificate ARN"
  }
}

The target is the resource address; values are the attributes you want to force. Everything else about that resource still comes from the mock. This keeps the broad convenience of mock_provider while making one value deterministic.

Reach for it when: a mocked resource’s auto-generated value is not good enough and a downstream reference needs an exact, known value.

override_data: Pin One Data Source

override_data is the data-source twin of override_resource. It replaces the computed result of a single data block with literals you supply. This is the go-to for external lookups and remote state that you do not want the test to actually perform.

mock_provider "aws" {}

run "ami_lookup_is_deterministic" {
  command = plan

  override_data {
    target = data.aws_ami.ubuntu
    values = {
      id = "ami-0123456789"
    }
  }

  assert {
    condition     = aws_instance.web.ami == "ami-0123456789"
    error_message = "instance did not use the overridden AMI id"
  }
}

The same block works for data.terraform_remote_state.<name>, where you nest the fake values under an outputs key. override_data guarantees the read is offline and the value is fixed.

Reach for it when: a specific data source (an AMI lookup, an IAM policy document, remote state) must return a known value and must not touch a real API.

Side-by-Side Comparison

PrimitiveScopeTargetsTypical use
mock_providerWhole providerAll resources + data sourcesDefault offline unit test
override_resourceOne resourceA single resource addressForce a specific computed resource value
override_dataOne data sourceA single data addressForce a lookup or remote-state result

They compose. The idiomatic pattern is a broad mock_provider plus a few surgical override_data / override_resource blocks for the handful of values you actually assert on:

mock_provider "aws" {}

override_data {
  target = data.aws_caller_identity.current
  values = { account_id = "111111111111" }
}

run "policy_targets_current_account" {
  command = plan
  assert {
    condition     = strcontains(aws_iam_policy.app.policy, "111111111111")
    error_message = "policy did not reference the mocked account id"
  }
}

Scoping: Run-Level vs File-Level

All three override blocks can sit inside a run block (applies to that run only) or at file scope (applies to every run). Put shared fakes at file scope to avoid repetition; keep run-specific values inside the run. mock_provider is always declared at file scope.

terraform test
tests/main.tftest.hcl... in progress
  run "policy_targets_current_account"... pass
tests/main.tftest.hcl... pass

Success! 1 passed, 0 failed.

A Simple Rule of Thumb

Start with mock_provider for everything. The moment an assertion depends on an exact value that the mock generates randomly, add an override_resource (for a managed resource) or override_data (for a data source) to pin just that value. Do not enumerate overrides you do not assert against, and do not reach for narrow overrides when a whole-provider mock would do.

For prompt-driven help authoring these blocks, the DevOps AI prompt library has Terraform test templates ready to adapt.

Frequently Asked Questions

When should I use mock_provider vs override_data? Use mock_provider to fake an entire provider so the whole plan runs offline. Use override_data when one specific data source must return a known value. They are complementary: mock broadly, then override the individual data reads you assert against.

What is the difference between override_resource and override_data? override_resource pins the computed values of a managed resource; override_data pins the computed values of a data source. Same syntax (target plus values), different block type. Choose based on whether the address is a resource or a data source.

Do I still need mock_provider if I use override blocks? Usually yes. Override blocks only replace the specific addresses you name. Without mock_provider, every other resource and data source still tries to reach the real API. Mock the provider for full isolation, then override individual values.

Can I use all three in one test file? Yes, and that is the recommended pattern. Declare mock_provider at file scope for broad isolation, then add override_resource and override_data blocks for the exact values your assertions depend on.

Where do override blocks go, in run or at file scope? Either. Put run-specific values inside the run block and shared values at file scope so they apply to every run. mock_provider is always file scope. 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.