DEV Community

Cover image for Automating Terraform Testing: From Unit Tests to End-to-End Validation
Victor Robin
Victor Robin

Posted on

Automating Terraform Testing: From Unit Tests to End-to-End Validation

Overview

Manual testing is essential when you are just starting out, but it does not scale. The moment your infrastructure grows beyond what one person can test in an afternoon, you need automated tests. Infrastructure that is tested automatically on every commit is infrastructure you can deploy with absolute confidence.

In this guide, we will break down the three layers of Terraform automated testing Unit, Integration, and End-to-End (E2E), and build a CI/CD pipeline that runs them automatically using GitHub Actions and secure AWS OIDC authentication.

Prerequisites

To follow along and run this code yourself, you will need:

  • An AWS Account, Terraform installed (v1.6.0 or newer for native testing)
  • Go installed (v1.21 or newer for Terratest)
  • A GitHub account to run the CI/CD pipeline

The Testing Strategy & Tradeoffs

A mature testing strategy doesn't rely on just one tool; it uses a pyramid approach balancing speed, cost, and thoroughness.

1. Unit Tests (terraform test)

Terraform 1.6+ ships with a native testing framework using .tftest.hcl files.

  • How it works: It runs a terraform plan in memory and evaluates your logic, variable validation, and configuration rules.
  • Tradeoffs: They are lightning-fast (seconds) and completely free because they do not deploy real infrastructure. However, they test less, they cannot catch cloud provider API errors or verify if an EC2 instance actually boots properly.

2. Integration Tests (Terratest)

Integration tests are written in Go using the Gruntwork Terratest library. They test individual, reusable modules in a sandbox.

  • How it works: It runs terraform apply, waits for the infrastructure to boot, makes real HTTP requests to your Load Balancers to verify the app is running, and strictly enforces a terraform destroy at the end to clean up.
  • Tradeoffs: Thorough, but slower (5–15 minutes) and incurs minor cloud costs since real AWS resources are provisioned.

3. End-to-End (E2E) Tests (Terratest)

E2E tests validate your final, composed architecture (VPC + DB + App) working together.

  • How it works: Deploys your entire production-like stack.
  • Tradeoffs: Highly accurate but very slow (15–30 minutes) and costly. Warning: Never run an E2E test with a destroy command against your live production state file. E2E tests should be run in isolated sandbox accounts using randomized naming to prevent collisions.

The Golden Rule: Use all three. Run Unit Tests on every Pull Request for instant feedback. Run Integration Tests when merging to your main branch. Run E2E tests in a sandbox environment before major releases.


Step-by-Step Guide: Running the Tests

Want to see this in action? Clone the repository and try it yourself.

Step 1: Clone the Repository


git clone https://github.com/Vivixell/aws-blue-green-infra/.git

cd aws-blue-green-infra

Enter fullscreen mode Exit fullscreen mode

Step 2: Run the Unit Tests

Navigate to the module directory and run the native Terraform test command. This will validate our strict security group rules and Blue/Green capacity logic without deploying anything.


cd modules/webserver
terraform init
terraform test

Enter fullscreen mode Exit fullscreen mode

You should see a fast, green output indicating all assertions passed.

Step 3: Run the Integration Tests

Integration tests deploy real resources. Ensure you have your AWS credentials configured locally, then execute the Go test suite.

cd ../../test
go mod tidy

# We explicitly target the Integration test to avoid running E2E tests locally
go test -v -timeout 45m -run TestWebserverClusterIntegration ./...

Enter fullscreen mode Exit fullscreen mode

Grab a coffee! This will take about 10 minutes to deploy the VPC and ASG, hit the Load Balancer with HTTP requests, and tear it all back down.

Step 4: The CI/CD Pipeline

Running tests locally is great, but automation is the ultimate goal. In the .github/workflows/terraform-test.yml file, we have a pipeline that ties this all together.

Instead of hardcoding highly privileged AWS Access Keys in GitHub (a major security risk), we use OpenID Connect (OIDC). GitHub requests a temporary, short-lived token directly from AWS.

Here is a snippet of how the pipeline is structured to run Unit Tests on PRs, and Integration tests on merges:


jobs:
  unit-tests:
    name: Unit Tests
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Configure AWS Credentials via OIDC
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: us-east-1
      - run: terraform init
      - run: terraform test

  integration-tests:
    name: Integration Tests
    runs-on: ubuntu-latest
    if: github.event_name == 'push' # Only runs when merged to main
    needs: unit-tests
    steps:
      - uses: actions/checkout@v4
      - name: Configure AWS Credentials via OIDC
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: us-east-1
      - uses: hashicorp/setup-terraform@v3
      - uses: actions/setup-go@v4
      - run: |
          go mod tidy
          go test -v -timeout 45m -run TestWebserverClusterIntegration ./...

Enter fullscreen mode Exit fullscreen mode

Conclusion

By implementing terraform test for fast syntax checking, Terratest for real-world validation, and GitHub Actions for continuous integration, you transform your infrastructure from fragile scripts into reliable, enterprise-grade software.

Happy testing!

Top comments (0)