Open-Source Wikis

/

Terraform

/

How to contribute

/

Testing

hashicorp/terraform

Testing

The Terraform Core test suite is large, mostly offline, and organized by layer. This page describes how to run tests, what kinds of tests exist, and the patterns the engine tests use.

Layout

Tier Location Run with
Unit tests *_test.go next to the code under test go test ./...
Engine integration tests internal/terraform/context_*_test.go go test ./internal/terraform/...
Configuration parser tests internal/configs/*_test.go + testdata/ go test ./internal/configs/...
Acceptance (external services) gated by TF_ACC=1 TF_ACC=1 go test ./internal/getproviders/...
Backend acceptance gated by TF_ACC=1 and per-backend env vars TF_ACC=1 ... go test ./internal/backend/remote-state/s3/...
End-to-end CLI internal/command/e2etest/ go test ./internal/command/e2etest/ (builds a real binary)
HCP Terraform e2e internal/backend/remote/e2e/ requires HCP credentials
Native test framework exercised via internal/moduletest/run_test.go and the e2e tests go test ./internal/moduletest/...

Running the suite

The fast path:

go test ./...

This is offline-only on the default TF_ACC setting and takes several minutes. To narrow down:

go test ./internal/configs/...
go test ./internal/terraform -run TestContext2Plan_dataSource
go test ./internal/command -run TestPlan_destroy -v

Engine tests: the Context pattern

Most tests for the graph engine instantiate a terraform.Context, build a configuration in-memory or from a fixture in internal/terraform/testdata/, run Plan or Apply, and assert on the resulting *plans.Plan or *states.State.

Skeleton:

m := testModule(t, "apply-good")
ctx := testContext2(t, &ContextOpts{
    Providers: map[addrs.Provider]providers.Factory{
        addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
    },
})
plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
assertNoDiagnostics(t, diags)
state, diags := ctx.Apply(plan, m, nil)
assertNoDiagnostics(t, diags)

Helpers like testModule, testContext2, testProviderFuncFixed, assertNoDiagnostics, assertResourceInstanceCreated, etc. are defined in internal/terraform/terraform_test.go and the _test.go files near each scenario.

Fixtures live in internal/terraform/testdata/<scenario-name>/. Almost 400 directories. The test loads <scenario-name> by name; the directory contains real .tf files.

Using mock providers

Engine tests mock providers with MockProvider (internal/providers/mock.go). It exposes hooks for every gRPC call:

p := testProvider("aws")
p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
    return providers.PlanResourceChangeResponse{
        PlannedState: req.ProposedNewState,
    }
}

Set GetProviderSchemaResponse to declare which resource types the mock implements. The pattern repeats hundreds of times across internal/terraform/context_*_test.go.

Configuration tests: the parser test data

internal/configs/testdata/ contains directories of .tf files that exercise the parser. Tests typically call configs.Parser.LoadConfigDir against a fixture and assert the resulting configs.Module or its diagnostics.

CLI tests

CLI tests in internal/command/*_test.go mock the streams and run a cli.Command.Run with a fake command.Meta. Each command's tests exercise flag parsing, view rendering (both human and JSON), and error paths.

For the init command in particular (internal/command/init_test.go, 215 KB), the tests cover an enormous matrix of cases: provider installation modes, backend transitions, lock-file regeneration, and module installation.

E2E tests

internal/command/e2etest/ builds the terraform binary as part of its setup and runs real shell commands against it, capturing stdout/stderr. These tests are slow but catch regressions in argument parsing, signal handling, and shell-completion plumbing that unit tests miss.

Toggle them with the build tag at the top of those files (or just run the package directly).

Acceptance tests

Tests that contact real services use TF_ACC=1:

TF_ACC=1 go test ./internal/getproviders/...
TF_ACC=1 go test ./internal/initwd/...

Backend acceptance suites additionally need provider credentials:

TF_ACC=1 \
  AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... AWS_DEFAULT_REGION=... \
  go test ./internal/backend/remote-state/s3/...

External services drift; pre-existing failures are common. Always run an unmodified copy of the suite before blaming your changes.

The terraform test framework

Terraform's built-in testing framework (terraform test subcommand) is exercised in two ways:

  1. Engine-level tests in internal/moduletest/run_test.go cover the runner, the assertions, mock-provider integration, and the per-run state lifecycle.
  2. CLI-level tests in internal/command/test_test.go (172 KB) drive the test command end-to-end against fixture configurations.

When changing the test framework itself, update both layers.

Linters as tests

The make staticcheck and make exhaustive targets are part of the effective test suite — CI fails on warnings. New nolint directives need a comment justifying the exemption.

Tips

  • t.Parallel() is used liberally in engine tests; respect it when adding new ones.
  • Use assertDiagnosticsMatch/assertDiagnosticCount from the test helpers rather than len(diags) == N — diagnostic ordering is not guaranteed.
  • When a test fails because state or plan output isn't equal, add cmp.Diff(...) style printing — most assertions in the engine tests already do this, but ad-hoc comparisons should follow the pattern.
  • Race-detector builds (go test -race ./internal/terraform/...) catch most concurrency bugs the graph walker introduces; CI runs with -race.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Testing – Terraform wiki | Factory