Overview

9 Testing and Refactoring

This chapter explains why automated testing is essential for Infrastructure as Code and how it changes the way teams build with Terraform and OpenTofu. Testing improves quality, speeds up pull request reviews, safeguards backward compatibility, and—crucially—boosts team psychological safety. It outlines what to test (your logic, data transformations, regexes, dynamic blocks, and end-to-end system behavior) rather than revalidating providers, and contrasts IaC testing with application testing: provisioning takes time and money, so tests must run efficiently and clean up thoroughly. The chapter also clarifies the predominance of integration tests in IaC, while noting where lighter-weight checks or provider-side unit tests apply.

In practice, the chapter promotes building runnable examples that double as both documentation and test fixtures, then shows how to run tests concurrently by randomizing names, tune timeouts, schedule automatic cleanup of stray resources, and use sound authentication patterns (like OIDC) in CI. It compares Terratest (mature, Go-based with many helper libraries and strong ecosystem) to the native Terraform Testing Framework (HCL-based, integrated, easier to adopt but tightly coupled to tool versions and newer features like mocks). The chapter walks through applying/destroying and asserting with Terratest, organizing make targets and matrix CI to test multiple engines and versions, and then mirrors that flow for the Terraform Testing Framework—covering run/variables/assert blocks, direct access to named values, plan vs apply constraints, provider/data/resource mocks to speed logic checks, and strategies for running example-based tests at scale. It also notes that Copilot currently helps more with Go/Terratest than with the newer native framework.

The refactoring section distinguishes internal changes (reorganization, file layout, clearer naming) from external ones (interface changes that may break consumers). It shows how to safely rename resources and modules with moved blocks, and how to rename variables via a parallel change (temporarily supporting old and new names through locals) to maintain compatibility. For unavoidable breaking changes, it recommends batching them into planned major releases, using clear upgrade notes, and aligning with provider major version shifts; meanwhile, maintain previous major versions with targeted fixes. The overall message: invest in reliable tests, use examples to ground real scenarios, automate CI and cleanup, and refactor methodically to evolve modules without surprising users.

IaC Testing Flow
Major Release Git Branching

Summary

  • Testing your modules provides a safety net for your systems and psychological safety for your users.
  • Automated test suites make it easier for developers to review pull requests and identify breaking changes.
  • Infrastructure as Code has many challenges when it comes to testing, such as longer runtime and orphaned resources.
  • Terratest is the most mature and capable testing framework for Terraform, but is written in Go.
  • The Terraform Testing Framework is new but promises developers a fully Terraform centric experience.
  • As projects mature they often need to be refactored in order to reduce tech debt and improve feature development.
  • Changes that break backwards compatibility should be carefully managed.

FAQ

What are the main benefits of automated testing for Terraform and OpenTofu projects?

Automated tests improve module quality by catching bugs early, speed up pull request reviews, and help maintain backward compatibility by failing when breaking changes are introduced. Just as importantly, a solid test suite boosts team psychological safety, giving developers confidence to refactor and ship changes without fear of regressions.

What should I test in Terraform modules—and what should I avoid?

Focus on testing your module’s logic and behavior, not the providers themselves. Good targets include data transformations, string construction, regex behavior, dynamic blocks (zero/one/many), and real system functionality (e.g., endpoints are reachable, credentials work). Avoid “pass-through” tests that merely restate provider behavior (e.g., asserting an input equals a corresponding provider attribute).

How is testing Infrastructure as Code different from traditional software testing?

Two big differences: time and cost. Creating real infrastructure often takes minutes to tens of minutes and costs money while resources run. This makes cleanup critical and motivates strategies like test grouping, concurrency-safe naming, and CI timeouts. Skipping tests carries its own costs, as outages from untested changes can be much more expensive.

Should I write unit tests or integration tests for Terraform?

Most Terraform tests are integration tests because Terraform configures external systems. Providers themselves generally have unit tests. Use integration tests to verify that resources and modules work together for various configurations, supplemented by small “unit-like” checks of your data transformations and logic.

When should I use Terratest versus the Terraform Testing Framework?

Use the native Terraform Testing Framework if your team prefers HCL, wants first-party integration, and can target Terraform/OpenTofu 1.6+. It can assert on internal values without outputs and supports mocks (1.7+). Choose Terratest if you need broad version flexibility, rich helper libraries, community ecosystem, or want to leverage Go and third-party packages. Existing teams with Terratest can continue with it confidently.

What are best practices for structuring and running IaC tests?
  • Follow a simple flow: pick config → apply → validate → destroy.
  • Use working examples as test inputs; group multiple assertions per environment to save time.
  • Run tests concurrently; add randomness (e.g., random_string) to resource names to avoid collisions.
  • Set generous timeouts in test runners and CI to prevent orphaned resources.
  • Automate cleanup (e.g., vendor “nuke” tools) in isolated test accounts; never in prod.
  • Use secure auth in CI, preferably OIDC.
  • Treat tests as code: clear names, comments, refactor when needed.
How can I make Terratest effective locally and in CI?

Follow a consistent pattern: build terraform.Options, defer destroy, run init/apply, then assert (outputs or real endpoints). Parameterize the Terraform/OpenTofu binary via an env var, and always pass a longer test timeout (e.g., 60m). Use Terratest’s helper modules (cloud providers, HTTP/DNS, retries). In CI, use a matrix to test multiple engines/versions and optionally run each test function separately for cleaner logs and faster parallelism. A Makefile target can hide the boilerplate (timeouts, go mod tidy, env vars).

How do native Terraform tests work, and what are mocks?

Write tests in .tftest.hcl with variables, run, and assert blocks. Assertions can access outputs and named values (e.g., resource attributes, locals) directly—no need to expose outputs. Use command = plan when you only need planned values, but remember plan can’t assert unknowns. Mocks (1.7+, beta) replace providers with fake values to speed logic checks and reduce cost; you can override defaults via mock_provider, mock_data, mock_resource, and override_* blocks, or reuse definitions via tfmock.hcl. Keep in mind mocks can drift from reality—use them to supplement, not replace, integration tests.

Can GitHub Copilot help write Terraform tests?

Copilot performs well with Terratest (Go) and can draft useful assertions and patterns you adapt into your test skeletons. It’s currently weak at generating native Terraform Test Framework files and may suggest outdated/less robust patterns. Use it for inspiration, then refine: keep your chosen structure, add robust options (e.g., retryable errors), and have experienced reviewers sanity-check generated code.

How should I approach refactoring modules and managing breaking changes?
  • Internal refactoring (reorganizing files, improving names, adding validation) should not change inputs/outputs and should not break tests—do this regularly.
  • Rename resources via moved blocks to avoid recreation; don’t remove those blocks casually.
  • Rename variables with “expand and contract”: add the new var, deprecate the old (default null), then use a local to prefer the old if set; remove deprecated vars in the next major release.
  • Batch breaking changes into planned major versions; publish upgrade notes (CHANGELOG/UPGRADE). Maintain prior major versions for critical fixes while teams migrate.

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Terraform in Depth ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Terraform in Depth ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Terraform in Depth ebook for free