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 -vEngine 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:
- Engine-level tests in
internal/moduletest/run_test.gocover the runner, the assertions, mock-provider integration, and the per-run state lifecycle. - CLI-level tests in
internal/command/test_test.go(172 KB) drive thetestcommand 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/assertDiagnosticCountfrom the test helpers rather thanlen(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.