Integration Testing

Integration testing for Terraform involves testing multiple modules together and verifying their interactions. This guide covers modern integration testing approaches for infrastructure as of 2025.

Overview

Integration tests verify that multiple Terraform modules work together correctly, testing:

  • Resource dependencies

  • Network connectivity

  • Service interactions

  • Cross-module variables

  • State management

Test Implementation

1. Module Integration Tests

module "networking" {
  source = "../modules/networking"
  
  vpc_cidr     = "10.0.0.0/16"
  environment  = "test"
}

module "database" {
  source = "../modules/database"
  
  vpc_id              = module.networking.vpc_id
  subnet_ids          = module.networking.private_subnet_ids
  security_group_ids  = [module.networking.database_security_group_id]
}

module "application" {
  source = "../modules/application"
  
  vpc_id              = module.networking.vpc_id
  subnet_ids          = module.networking.private_subnet_ids
  database_endpoint   = module.database.endpoint
  database_port       = module.database.port
}

2. Testing with Terratest

package test

import (
    "testing"
    "github.com/gruntwork-io/terratest/modules/terraform"
    "github.com/gruntwork-io/terratest/modules/http-helper"
    "github.com/stretchr/testify/assert"
)

func TestIntegration(t *testing.T) {
    terraformOptions := &terraform.Options{
        TerraformDir: "../examples/complete",
        Vars: map[string]interface{}{
            "environment": "test",
            "region":     "us-west-2",
        },
    }

    defer terraform.Destroy(t, terraformOptions)
    terraform.InitAndApply(t, terraformOptions)

    // Test VPC and Subnet Creation
    vpcId := terraform.Output(t, terraformOptions, "vpc_id")
    subnetIds := terraform.OutputList(t, terraformOptions, "private_subnet_ids")
    assert.NotEmpty(t, vpcId)
    assert.Equal(t, 3, len(subnetIds))

    // Test Database Connectivity
    dbEndpoint := terraform.Output(t, terraformOptions, "database_endpoint")
    dbPort := terraform.Output(t, terraformOptions, "database_port")
    
    // Test Application Health
    appUrl := terraform.Output(t, terraformOptions, "application_url")
    http_helper.HttpGetWithRetry(t, appUrl, nil, 200, "OK", 30, 5*time.Second)
}

Best Practices

1. Test Environment Isolation

provider "aws" {
  region = var.region
  
  default_tags {
    tags = {
      Environment = "test"
      Terraform   = "true"
      Test       = "integration"
    }
  }
  
  # Use separate credentials for tests
  assume_role {
    role_arn = "arn:aws:iam::ACCOUNT_ID:role/terraform-integration-tests"
  }
}

2. Data Cleanup

func TestMain(m *testing.M) {
    // Setup test environment
    setup()
    
    // Run tests
    code := m.Run()
    
    // Cleanup
    cleanup()
    
    os.Exit(code)
}

func cleanup() {
    // Custom cleanup logic
    cleanupDatabases()
    cleanupStorageBuckets()
    cleanupLoadBalancers()
}

3. Test Stages

func TestIntegrationStages(t *testing.T) {
    // Stage 1: Infrastructure Setup
    networkingOptions := setupNetworking(t)
    defer terraform.Destroy(t, networkingOptions)
    
    // Stage 2: Database Deployment
    databaseOptions := setupDatabase(t, networkingOptions)
    defer terraform.Destroy(t, databaseOptions)
    
    // Stage 3: Application Deployment
    applicationOptions := setupApplication(t, networkingOptions, databaseOptions)
    defer terraform.Destroy(t, applicationOptions)
    
    // Integration Tests
    runIntegrationTests(t, applicationOptions)
}

Advanced Testing Scenarios

1. Cross-Region Testing

func TestCrossRegion(t *testing.T) {
    primaryOptions := &terraform.Options{
        TerraformDir: "../examples/primary",
        Vars: map[string]interface{}{
            "region": "us-west-2",
        },
    }
    
    secondaryOptions := &terraform.Options{
        TerraformDir: "../examples/secondary",
        Vars: map[string]interface{}{
            "region": "us-east-1",
        },
    }
    
    // Deploy both regions
    terraform.InitAndApply(t, primaryOptions)
    terraform.InitAndApply(t, secondaryOptions)
    
    // Test cross-region functionality
    testReplication(t, primaryOptions, secondaryOptions)
    testFailover(t, primaryOptions, secondaryOptions)
}

2. Load Testing

func TestLoadBalancing(t *testing.T) {
    options := &terraform.Options{
        TerraformDir: "../examples/load-balanced",
    }
    
    terraform.InitAndApply(t, options)
    
    endpoint := terraform.Output(t, options, "lb_endpoint")
    
    // Run load tests
    loadTest := &vegeta.Attack{
        Rate: vegeta.Rate{Freq: 100, Per: time.Second},
        Duration: 5 * time.Minute,
        Targeter: vegeta.NewStaticTargeter(vegeta.Target{
            Method: "GET",
            URL:    endpoint,
        }),
    }
    
    metrics := vegeta.Attack(loadTest)
    assert.Less(t, metrics.Latencies.P95, 500*time.Millisecond)
}

Monitoring and Debugging

1. Test Logging

func TestWithLogging(t *testing.T) {
    logger := terratest.NewLogger(t)
    logger.Logf(t, "Starting integration tests...")
    
    options := &terraform.Options{
        TerraformDir: "../examples/complete",
        Logger:       logger,
    }
    
    defer terraform.Destroy(t, options)
    terraform.InitAndApply(t, options)
    
    logger.Logf(t, "Infrastructure deployed successfully")
}

2. Resource Health Checks

func checkResourceHealth(t *testing.T, options *terraform.Options) {
    resourceIds := terraform.OutputMap(t, options, "resource_ids")
    
    for name, id := range resourceIds {
        status := getResourceStatus(id)
        assert.Equal(t, "healthy", status, "Resource %s is not healthy", name)
    }
}

CI/CD Pipeline Integration

name: Integration Tests
on:
  pull_request:
    branches: [ main ]
  push:
    branches: [ main ]

jobs:
  integration-test:
    runs-on: ubuntu-latest
    environment: test
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::ACCOUNT_ID:role/github-actions-role
          aws-region: us-west-2
      
      - name: Setup Go
        uses: actions/setup-go@v4
        with:
          go-version: '1.21'
      
      - name: Run Integration Tests
        run: |
          cd test/integration
          go test -v -timeout 2h ./...
        env:
          TF_VAR_environment: test

Last updated